root/usr.bin/brandelf/brandelf.c
/*-
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 2000, 2001 David O'Brien
 * Copyright (c) 1996 Søren Schmidt
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/elf_common.h>
#include <sys/errno.h>

#include <capsicum_helpers.h>
#include <err.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libcasper.h>
#include <casper/cap_fileargs.h>

static int elftype(const char *);
static const char *iselftype(int);
static void printelftypes(void);
static void usage(void) __dead2;

struct ELFtypes {
        const char *str;
        int value;
};
/* XXX - any more types? */
static struct ELFtypes elftypes[] = {
        { "FreeBSD",    ELFOSABI_FREEBSD },
        { "Linux",      ELFOSABI_LINUX },
        { "Solaris",    ELFOSABI_SOLARIS },
        { "SVR4",       ELFOSABI_SYSV }
};

int
main(int argc, char **argv)
{

        const char *strtype = "FreeBSD";
        int ch, flags, retval, type;
        bool change, force, listed;
        fileargs_t *fa;
        cap_rights_t rights;

        type = ELFOSABI_FREEBSD;
        retval = 0;
        change = false;
        force = false;
        listed = false;

        while ((ch = getopt(argc, argv, "f:lt:v")) != -1)
                switch (ch) {
                case 'f':
                        if (change)
                                errx(1, "f option incompatible with t option");
                        force = true;
                        type = atoi(optarg);
                        if (errno == ERANGE || type < 0 || type > 255) {
                                warnx("invalid argument to option f: %s",
                                    optarg);
                                usage();
                        }
                        break;
                case 'l':
                        printelftypes();
                        listed = true;
                        break;
                case 'v':
                        /* does nothing */
                        break;
                case 't':
                        if (force)
                                errx(1, "t option incompatible with f option");
                        change = true;
                        strtype = optarg;
                        break;
                default:
                        usage();
        }
        argc -= optind;
        argv += optind;
        if (argc == 0) {
                if (listed)
                        exit(0);
                else {
                        warnx("no file(s) specified");
                        usage();
                }
        }

        if (!force && (type = elftype(strtype)) == -1) {
                warnx("invalid ELF type '%s'", strtype);
                printelftypes();
                usage();
        }

        flags = change || force ? O_RDWR : O_RDONLY;
        cap_rights_init(&rights, CAP_READ, CAP_SEEK);
        if (flags == O_RDWR)
                cap_rights_set(&rights, CAP_WRITE);

        fa = fileargs_init(argc, argv, flags, 0, &rights, FA_OPEN);
        if (fa == NULL)
                err(1, "unable to init casper");

        caph_cache_catpages();
        if (caph_limit_stdio() < 0 || caph_enter_casper() < 0)
                err(1, "unable to enter capability mode");

        while (argc != 0) {
                int fd;
                char buffer[EI_NIDENT];

                if ((fd = fileargs_open(fa, argv[0])) < 0) {
                        warn("error opening file %s", argv[0]);
                        retval = 1;
                        goto fail;
                }
                if (read(fd, buffer, EI_NIDENT) < EI_NIDENT) {
                        warnx("file '%s' too short", argv[0]);
                        retval = 1;
                        goto fail;
                }
                if (buffer[0] != ELFMAG0 || buffer[1] != ELFMAG1 ||
                    buffer[2] != ELFMAG2 || buffer[3] != ELFMAG3) {
                        warnx("file '%s' is not ELF format", argv[0]);
                        retval = 1;
                        goto fail;
                }
                if (!change && !force) {
                        fprintf(stdout,
                                "File '%s' is of brand '%s' (%u).\n",
                                argv[0], iselftype(buffer[EI_OSABI]),
                                buffer[EI_OSABI]);
                        if (!iselftype(type)) {
                                warnx("ELF ABI Brand '%u' is unknown",
                                      type);
                                printelftypes();
                        }
                }
                else {
                        buffer[EI_OSABI] = type;
                        lseek(fd, 0, SEEK_SET);
                        if (write(fd, buffer, EI_NIDENT) != EI_NIDENT) {
                                warn("error writing %s %d", argv[0], fd);
                                retval = 1;
                                goto fail;
                        }
                }
fail:
                close(fd);
                argc--;
                argv++;
        }

        fileargs_free(fa);
        return (retval);
}

static void
usage(void)
{
        (void)fprintf(stderr,
            "usage: brandelf [-lv] [-f ELF_ABI_number] [-t string] file ...\n");
        exit(1);
}

static const char *
iselftype(int etype)
{
        size_t elfwalk;

        for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++)
                if (etype == elftypes[elfwalk].value)
                        return (elftypes[elfwalk].str);
        return (0);
}

static int
elftype(const char *elfstrtype)
{
        size_t elfwalk;

        for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++)
                if (strcasecmp(elfstrtype, elftypes[elfwalk].str) == 0)
                        return (elftypes[elfwalk].value);
        return (-1);
}

static void
printelftypes(void)
{
        size_t elfwalk;

        fprintf(stderr, "known ELF types are: ");
        for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++)
                fprintf(stderr, "%s(%u) ", elftypes[elfwalk].str,
                    elftypes[elfwalk].value);
        fprintf(stderr, "\n");
}