Annotation of embedaddon/strongswan/src/sec-updater/sec-updater.c, revision 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>