Annotation of embedaddon/strongswan/src/sec-updater/sec-updater.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2012-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
                     17: #include <getopt.h>
                     18: #include <unistd.h>
                     19: #include <stdio.h>
                     20: #include <string.h>
                     21: #include <errno.h>
                     22: #include <syslog.h>
                     23: #include <time.h>
                     24: #include <sys/stat.h>
                     25: #include <stdlib.h>
                     26: 
                     27: #include <library.h>
                     28: #include <utils/debug.h>
                     29: 
                     30: #define EXIT_NO_UPDATES                80
                     31: #define TMP_DEB_FILE           "/tmp/sec-updater.deb"
                     32: #define TMP_TAG_FILE           "/tmp/sec-updater.tag"
                     33: #define SWID_GEN_CMD           "/usr/local/bin/swid_generator"
                     34: #define TNC_MANAGE_CMD         "/var/www/tnc/manage.py"
                     35: 
                     36: typedef enum sec_update_state_t sec_update_state_t;
                     37: 
                     38: enum sec_update_state_t {
                     39:        SEC_UPDATE_STATE_BEGIN_PACKAGE,
                     40:        SEC_UPDATE_STATE_VERSION,
                     41:        SEC_UPDATE_STATE_FILENAME,
                     42:        SEC_UPDATE_STATE_END_PACKAGE
                     43: };
                     44: 
                     45: typedef struct stats_t stats_t;
                     46: 
                     47: struct stats_t {
                     48:        time_t release;
                     49:        int product;
                     50:        int packages;
                     51:        int new_versions;
                     52:        int updated_versions;
                     53: };
                     54: 
                     55: /**
                     56:  * global debug output variables
                     57:  */
                     58: static int debug_level = 1;
                     59: static bool stderr_quiet = FALSE;
                     60: 
                     61: /**
                     62:  * sec_updater dbg function
                     63:  */
                     64: static void sec_updater_dbg(debug_t group, level_t level, char *fmt, ...)
                     65: {
                     66:        int priority = LOG_INFO;
                     67:        char buffer[8192];
                     68:        char *current = buffer, *next;
                     69:        va_list args;
                     70: 
                     71:        if (level <= debug_level)
                     72:        {
                     73:                if (!stderr_quiet)
                     74:                {
                     75:                        va_start(args, fmt);
                     76:                        vfprintf(stderr, fmt, args);
                     77:                        fprintf(stderr, "\n");
                     78:                        va_end(args);
                     79:                }
                     80: 
                     81:                /* write in memory buffer first */
                     82:                va_start(args, fmt);
                     83:                vsnprintf(buffer, sizeof(buffer), fmt, args);
                     84:                va_end(args);
                     85: 
                     86:                /* do a syslog with every line */
                     87:                while (current)
                     88:                {
                     89:                        next = strchr(current, '\n');
                     90:                        if (next)
                     91:                        {
                     92:                                *(next++) = '\0';
                     93:                        }
                     94:                        syslog(priority, "%s\n", current);
                     95:                        current = next;
                     96:                }
                     97:        }
                     98: }
                     99: 
                    100: /**
                    101:  * atexit handler to close everything on shutdown
                    102:  */
                    103: static void cleanup(void)
                    104: {
                    105:        closelog();
                    106:        library_deinit();
                    107: }
                    108: 
                    109: static void usage(void)
                    110: {
                    111:        printf("\
                    112: Usage:\n\
                    113:   sec-updater --help\n\
                    114:   sec-updater [--debug <level>] [--quiet]  [--security] --os <string>\n\
                    115:                --arch <string> --uri <uri> --file <filename>\n\n\
                    116:   Options:\n\
                    117:     --help             print usage information\n\
                    118:     --debug <level>    set debug level\n\
                    119:     --quiet            suppress debug output to stderr\n\
                    120:     --os <string>      operating system\n\
                    121:     --arch <string>    hw architecture\n\
                    122:     --security         set when parsing a file with security updates\n\
                    123:     --file <filename>  package information file to parse\n\
                    124:     --uri <uri>        uri where to download deb package from\n");
                    125:  }
                    126: 
                    127: /**
                    128:  * Update the package database
                    129:  */
                    130: static bool update_database(database_t *db, char *package, char *version,
                    131:                                                        bool security, stats_t *stats, bool *new)
                    132: {
                    133:        int pid = 0, vid = 0, sec_flag;
                    134:        bool first = TRUE, found = FALSE;
                    135:        char *release;
                    136:        enumerator_t *e;
                    137: 
                    138:        /* increment package count */
                    139:        stats->packages++;
                    140: 
                    141:        /* set new output variable */
                    142:        *new = FALSE;
                    143: 
                    144:        /* check if package is already in database */
                    145:        e = db->query(db, "SELECT id FROM packages WHERE name = ?",
                    146:                                          DB_TEXT, package, DB_INT);
                    147:        if (!e)
                    148:        {
                    149:                return FALSE;
                    150:        }
                    151:        if (!e->enumerate(e, &pid))
                    152:        {
                    153:                pid = 0;
                    154:        }
                    155:        e->destroy(e);
                    156: 
                    157:        if (!pid)
                    158:        {
                    159:                return TRUE;
                    160:        }
                    161: 
                    162:        /* retrieve all package versions stored in database */
                    163:        e = db->query(db,
                    164:                        "SELECT id, release, security FROM versions "
                    165:                        "WHERE product = ? AND package = ?",
                    166:                         DB_INT, stats->product,  DB_INT, pid, DB_INT, DB_TEXT, DB_INT);
                    167:        if (!e)
                    168:        {
                    169:                return FALSE;
                    170:        }
                    171: 
                    172:        while (e->enumerate(e, &vid, &release, &sec_flag))
                    173:        {
                    174:                char command[BUF_LEN];
                    175:                char found_char = ' ';
                    176:                bool update_version = FALSE;
                    177: 
                    178:                if (streq(version, release))
                    179:                {
                    180:                        found = TRUE;
                    181:                        found_char = '*';
                    182:                }
                    183:                else if (security)
                    184:                {
                    185:                         snprintf(command, BUF_LEN, "dpkg --compare-versions %s lt %s",
                    186:                                                                                 release, version);
                    187:                        if (system(command) == 0)
                    188:                        {
                    189:                                found_char = '!';
                    190:                                if (!sec_flag)
                    191:                                {
                    192:                                        if (db->execute(db, NULL, "UPDATE versions "
                    193:                                                "SET security = 1 WHERE id = ?", DB_INT, vid) != 1)
                    194:                                        {
                    195:                                                DBG1(DBG_IMV, "  could not update version");
                    196:                                                e->destroy(e);
                    197:                                                return FALSE;
                    198:                                        }
                    199:                                        update_version = TRUE;
                    200:                                        stats->updated_versions++;
                    201:                                }
                    202:                        }
                    203:                }
                    204:                if (debug_level < 2 && !update_version)
                    205:                {
                    206:                        continue;
                    207:                }
                    208:                if (first)
                    209:                {
                    210:                        DBG1(DBG_IMV, "%s", package);
                    211:                        first = FALSE;
                    212:                }
                    213:                DBG1(DBG_IMV, "  %c%s %s", found_char , sec_flag ? "s" : " ", release);
                    214:        }
                    215:        e->destroy(e);
                    216: 
                    217:        if (!found)
                    218:        {
                    219:                if (first)
                    220:                {
                    221:                        DBG1(DBG_IMV, "%s", package);
                    222:                }
                    223:                DBG1(DBG_IMV, "  +  %s", version);
                    224: 
                    225:                if (db->execute(db, &vid,
                    226:                        "INSERT INTO versions "
                    227:                        "(package, product, release, security, time) "
                    228:                        "VALUES (?, ?, ?, 0, ?)", DB_INT, pid, DB_INT, stats->product,
                    229:                        DB_TEXT, version, DB_INT, stats->release) != 1)
                    230:                {
                    231:                        DBG1(DBG_IMV, "  could not store version to database");
                    232:                        return FALSE;
                    233:                }
                    234:                stats->new_versions++;
                    235:                *new = TRUE;
                    236:        }
                    237: 
                    238:        return TRUE;
                    239: }
                    240: 
                    241: /**
                    242:  * Process a package file and store updates in the database
                    243:  */
                    244: static int process_packages(char *path, char *os, char *arch, char *uri,
                    245:                                                        bool security)
                    246: {
                    247:        char line[BUF_LEN], product[BUF_LEN], command[BUF_LEN];
                    248:        char *db_uri, *download_uri = NULL, *swid_regid, *swid_entity;
                    249:        char *pos, *package = NULL, *version = NULL, *filename = NULL;
                    250:        char *swid_gen_cmd, *tnc_manage_cmd, *tmp_deb_file, *tmp_tag_file;
                    251:        sec_update_state_t state;
                    252:        enumerator_t *e;
                    253:        database_t *db;
                    254:        int len, pid;
                    255:        chunk_t deb = chunk_empty;
                    256:        FILE *file;
                    257:        stats_t stats;
                    258:        bool success = TRUE, new;
                    259: 
                    260:        /* initialize statistics */
                    261:        memset(&stats, 0x00, sizeof(stats_t));
                    262: 
                    263:        /* Set release date to current time */
                    264:        stats.release = time(NULL);
                    265: 
                    266:        /* opening package file */
                    267:        file = fopen(path, "r");
                    268:        if (!file)
                    269:        {
                    270:                DBG1(DBG_IMV, "  could not open \"%s\"", path);
                    271:                exit(EXIT_FAILURE);
                    272:        }
                    273: 
                    274:        /* connect package database */
                    275:        db_uri = lib->settings->get_str(lib->settings, "sec-updater.database", NULL);
                    276:        if (!db_uri)
                    277:        {
                    278:                DBG1(DBG_IMV, "database URI sec-updater.database not set");
                    279:                fclose(file);
                    280:                exit(EXIT_FAILURE);
                    281:        }
                    282:        db = lib->db->create(lib->db, db_uri);
                    283:        if (!db)
                    284:        {
                    285:                DBG1(DBG_IMV, "could not connect to database '%s'", db_uri);
                    286:                fclose(file);
                    287:                exit(EXIT_FAILURE);
                    288:        }
                    289: 
                    290:        /* form product name by concatenating os and arch strings */
                    291:        snprintf(product, BUF_LEN, "%s %s", os, arch);
                    292: 
                    293:        /* check if product is already in database */
                    294:        e = db->query(db, "SELECT id FROM products WHERE name = ?",
                    295:                                  DB_TEXT, product, DB_INT);
                    296:        if (e)
                    297:        {
                    298:                if (e->enumerate(e, &pid))
                    299:                {
                    300:                        stats.product = pid;
                    301:                }
                    302:                e->destroy(e);
                    303:        }
                    304:        if (!stats.product)
                    305:        {
                    306:                if (db->execute(db, &pid, "INSERT INTO products (name) VALUES (?)",
                    307:                                                DB_TEXT, product) != 1)
                    308:                {
                    309:                        DBG1(DBG_IMV, "could not store product '%s' to database",
                    310:                                                         product);
                    311:                        fclose(file);
                    312:                        db->destroy(db);
                    313:                        exit(EXIT_FAILURE);
                    314:                }
                    315:                stats.product = pid;
                    316:        }
                    317: 
                    318:        /* get settings for the loop */
                    319:        swid_regid = lib->settings->get_str(lib->settings,
                    320:                                                "sec-updater.swid_gen.tag_creator.regid",
                    321:                                                "strongswan.org");
                    322:        swid_entity = lib->settings->get_str(lib->settings,
                    323:                                                "sec-updater.swid_gen.tag_creator.name",
                    324:                                                "strongSwan Project");
                    325:        swid_gen_cmd = lib->settings->get_str(lib->settings,
                    326:                                                "sec-updater.swid_gen.command", SWID_GEN_CMD);
                    327:        tnc_manage_cmd = lib->settings->get_str(lib->settings,
                    328:                                                "sec-updater.tnc_manage_command", TNC_MANAGE_CMD);
                    329:        tmp_deb_file = lib->settings->get_str(lib->settings,
                    330:                                                "sec-updater.tmp.deb_file", TMP_DEB_FILE);
                    331:        tmp_tag_file = lib->settings->get_str(lib->settings,
                    332:                                                "sec-updater.tmp.tag_file", TMP_TAG_FILE);
                    333: 
                    334:        state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
                    335: 
                    336:        while (fgets(line, sizeof(line), file))
                    337:        {
                    338:                /* set read pointer to beginning of line */
                    339:                pos = line;
                    340: 
                    341:                switch (state)
                    342:                {
                    343:                        case SEC_UPDATE_STATE_BEGIN_PACKAGE:
                    344:                                pos = strstr(pos, "Package: ");
                    345:                                if (!pos)
                    346:                                {
                    347:                                        continue;
                    348:                                }
                    349:                                pos += 9;
                    350:                                package = pos;
                    351:                                pos = strchr(pos, '\n');
                    352:                                if (pos)
                    353:                                {
                    354:                                        package = strndup(package, pos - package);
                    355:                                        state = SEC_UPDATE_STATE_VERSION;
                    356:                                }
                    357:                                break;
                    358:                        case SEC_UPDATE_STATE_VERSION:
                    359:                                pos = strstr(pos, "Version: ");
                    360:                                if (!pos)
                    361:                                {
                    362:                                        continue;
                    363:                                }
                    364:                                pos += 9;
                    365:                                version = pos;
                    366:                                pos = strchr(pos, '\n');
                    367:                                if (pos)
                    368:                                {
                    369:                                        version = strndup(version, pos - version);
                    370:                                        success = update_database(db, package, version, security,
                    371:                                                                                          &stats, &new);
                    372:                                        state = (success && new) ? SEC_UPDATE_STATE_FILENAME :
                    373:                                                                                           SEC_UPDATE_STATE_END_PACKAGE;
                    374:                                }
                    375:                                break;
                    376:                        case SEC_UPDATE_STATE_FILENAME:
                    377:                                pos = strstr(pos, "Filename: ");
                    378:                                if (!pos)
                    379:                                {
                    380:                                        continue;
                    381:                                }
                    382:                                state = SEC_UPDATE_STATE_END_PACKAGE;
                    383: 
                    384:                                pos += 10;
                    385:                                filename = pos;
                    386:                                pos = strchr(pos, '\n');
                    387:                                if (!pos)
                    388:                                {
                    389:                                        break;
                    390:                                }
                    391:                                len = pos - filename;
                    392:                                if (asprintf(&download_uri, "%s/%.*s", uri, len, filename) == -1)
                    393:                                {
                    394:                                        break;
                    395:                                }
                    396: 
                    397:                                /* retrieve deb package file from linux repository */
                    398:                                if (lib->fetcher->fetch(lib->fetcher, download_uri,
                    399:                                                                                                &deb, FETCH_END) != SUCCESS)
                    400:                                {
                    401:                                        DBG1(DBG_IMV, "     %s failed", download_uri);
                    402:                                        break;
                    403:                                }
                    404:                                DBG1(DBG_IMV, "     %s (%u bytes)", download_uri, deb.len);
                    405: 
                    406:                                /* store deb package file to temporary location */
                    407:                                if (!chunk_write(deb, tmp_deb_file, 0022, TRUE))
                    408:                                {
                    409:                                        DBG1(DBG_IMV, "     save to '%s' failed", tmp_deb_file);
                    410:                                        break;
                    411:                                }
                    412: 
                    413:                                /* generate SWID tag for downloaded deb package */
                    414:                                snprintf(command, BUF_LEN, "%s swid --full --package-file %s "
                    415:                                                 "--regid %s --entity-name '%s' --os '%s' --arch '%s' "
                    416:                                                 ">> %s", swid_gen_cmd, tmp_deb_file, swid_regid,
                    417:                                                 swid_entity, os, arch, tmp_tag_file);
                    418:                                if (system(command) != 0)
                    419:                                {
                    420:                                        DBG1(DBG_IMV, "     tag generation failed");
                    421:                                        break;
                    422:                                }
                    423:                                break;
                    424:                        case SEC_UPDATE_STATE_END_PACKAGE:
                    425:                                if (*pos != '\n')
                    426:                                {
                    427:                                        continue;
                    428:                                }
                    429:                                free(package);
                    430:                                free(version);
                    431:                                free(download_uri);
                    432:                                chunk_free(&deb);
                    433:                                package = version = download_uri = NULL;
                    434: 
                    435:                                if (!success)
                    436:                                {
                    437:                                        fclose(file);
                    438:                                        db->destroy(db);
                    439:                                        exit(EXIT_FAILURE);
                    440:                                }
                    441:                                state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
                    442:                }
                    443:        }
                    444: 
                    445:        free(package);
                    446:        free(version);
                    447:        free(download_uri);
                    448:        fclose(file);
                    449:        db->destroy(db);
                    450: 
                    451:        /* import swid tags into strongTNC */
                    452:        if (stats.new_versions > 0)
                    453:        {
                    454:                snprintf(command, BUF_LEN, "%s importswid %s",
                    455:                                 tnc_manage_cmd, tmp_tag_file);
                    456:                if (system(command) != 0)
                    457:                {
                    458:                        DBG1(DBG_IMV, "tag import failed");
                    459:                }
                    460:                snprintf(command, BUF_LEN, "rm %s %s",
                    461:                                 tmp_deb_file, tmp_tag_file);
                    462:                if (system(command) != 0)
                    463:                {
                    464:                        DBG1(DBG_IMV, "removing temporary files failed");
                    465:                }
                    466:        }
                    467: 
                    468:        DBG1(DBG_IMV, "processed \"%s\": %d packages, %d new versions, "
                    469:                                  "%d updated versions", path, stats.packages,
                    470:                                   stats.new_versions, stats.updated_versions);
                    471: 
                    472:        return (stats.new_versions + stats.updated_versions) ?
                    473:                        EXIT_SUCCESS : EXIT_NO_UPDATES;
                    474: }
                    475: 
                    476: static int do_args(int argc, char *argv[])
                    477: {
                    478:        char *filename = NULL, *arch = NULL, *os = NULL, *uri = NULL;
                    479:        bool security = FALSE;
                    480: 
                    481:        /* reinit getopt state */
                    482:        optind = 0;
                    483: 
                    484:        while (TRUE)
                    485:        {
                    486:                int c;
                    487: 
                    488:                struct option long_opts[] = {
                    489:                        { "help", no_argument, NULL, 'h' },
                    490:                        { "arch", required_argument, NULL, 'a' },
                    491:                        { "debug", required_argument, NULL, 'd' },
                    492:                        { "file", required_argument, NULL, 'f' },
                    493:                        { "os", required_argument, NULL, 'o' },
                    494:                        { "quiet", no_argument, NULL, 'q' },
                    495:                        { "security", no_argument, NULL, 's' },
                    496:                        { "uri", required_argument, NULL, 'u' },
                    497:                        { 0,0,0,0 }
                    498:                };
                    499: 
                    500:                c = getopt_long(argc, argv, "ha:d:f:o:qsu:", long_opts, NULL);
                    501:                switch (c)
                    502:                {
                    503:                        case EOF:
                    504:                                break;
                    505:                        case 'h':
                    506:                                usage();
                    507:                                exit(EXIT_SUCCESS);
                    508:                        case 'a':
                    509:                                arch = optarg;
                    510:                                continue;
                    511:                        case 'd':
                    512:                                debug_level = atoi(optarg);
                    513:                                continue;
                    514:                        case 'f':
                    515:                                filename = optarg;
                    516:                                continue;
                    517:                        case 'o':
                    518:                                os = optarg;
                    519:                                continue;
                    520:                        case 'q':
                    521:                                stderr_quiet = TRUE;
                    522:                                continue;
                    523:                        case 's':
                    524:                                security = TRUE;
                    525:                                continue;
                    526:                        case 'u':
                    527:                                uri = optarg;
                    528:                                continue;
                    529:                }
                    530:                break;
                    531:        }
                    532: 
                    533:        if (filename && os && arch && uri)
                    534:        {
                    535:                return process_packages(filename, os, arch, uri, security);
                    536:        }
                    537:        else
                    538:        {
                    539:                usage();
                    540:                exit(EXIT_FAILURE);
                    541:        }
                    542: }
                    543: 
                    544: int main(int argc, char *argv[])
                    545: {
                    546:        /* enable attest debugging hook */
                    547:        dbg = sec_updater_dbg;
                    548:        openlog("sec-updater", 0, LOG_DEBUG);
                    549: 
                    550:        atexit(cleanup);
                    551: 
                    552:        /* initialize library */
                    553:        if (!library_init(NULL, "sec-updater"))
                    554:        {
                    555:                exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
                    556:        }
                    557:        if (!lib->plugins->load(lib->plugins,
                    558:                        lib->settings->get_str(lib->settings, "sec-updater.load",
                    559:                                                                                                  "sqlite curl")))
                    560:        {
                    561:                exit(SS_RC_INITIALIZATION_FAILED);
                    562:        }
                    563:        exit(do_args(argc, argv));
                    564: }
                    565: 

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