[BACK]Return to fileproc.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / acme-client

File: [local] / src / usr.sbin / acme-client / fileproc.c (download)

Revision 1.18, Mon Jul 12 15:09:20 2021 UTC (2 years, 10 months ago) by beck
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, HEAD
Changes since 1.17: +2 -2 lines

Change the error reporting pattern throughout the tree when unveil
fails to report the path that the failure occured on. Suggested by
deraadt@ after some tech discussion.

Work done and verified by Ashton Fagg <ashton@fagg.id.au>

ok deraadt@ semarie@ claudio@

/*	$Id: fileproc.c,v 1.18 2021/07/12 15:09:20 beck Exp $ */
/*
 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/stat.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "extern.h"

static int
serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz)
{
	int	  fd;
	char	 *tmp;

	/* create backup hardlink */
	if (asprintf(&tmp, "%s.1", real) == -1) {
		warn("asprintf");
		return 0;
	}
	(void) unlink(tmp);
	if (link(real, tmp) == -1 && errno != ENOENT) {
		warn("link");
		free(tmp);
		return 0;
	}
	free(tmp);

	/*
	 * Write into backup location, overwriting.
	 * Then atomically do the rename.
	 */

	if (asprintf(&tmp, "%s.XXXXXXXXXX", real) == -1) {
		warn("asprintf");
		return 0;
	}
	if ((fd = mkstemp(tmp)) == -1) {
		warn("mkstemp");
		goto out;
	}
	if (fchmod(fd, 0444) == -1) {
		warn("fchmod");
		goto out;
	}
	if ((ssize_t)vsz != write(fd, v, vsz)) {
		warnx("write");
		goto out;
	}
	if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) {
		warnx("write");
		goto out;
	}
	if (close(fd) == -1)
		goto out;
	if (rename(tmp, real) == -1) {
		warn("%s", real);
		goto out;
	}

	free(tmp);
	return 1;
out:
	if (fd != -1)
		close(fd);
	(void) unlink(tmp);
	free(tmp);
	return 0;
}

int
fileproc(int certsock, const char *certdir, const char *certfile, const char
    *chainfile, const char *fullchainfile)
{
	char		*csr = NULL, *ch = NULL;
	size_t		 chsz, csz;
	int		 rc = 0;
	long		 lval;
	enum fileop	 op;

	if (unveil(certdir, "rwc") == -1) {
		warn("unveil %s", certdir);
		goto out;
	}

	/*
	 * rpath and cpath for rename, wpath and cpath for
	 * writing to the temporary. fattr for fchmod.
	 */
	if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) {
		warn("pledge");
		goto out;
	}

	/* Read our operation. */

	op = FILE__MAX;
	if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0)
		op = FILE_STOP;
	else if (lval == FILE_CREATE || lval == FILE_REMOVE)
		op = lval;

	if (FILE_STOP == op) {
		rc = 1;
		goto out;
	} else if (FILE__MAX == op) {
		warnx("unknown operation from certproc");
		goto out;
	}

	/*
	 * If revoking certificates, just unlink the files.
	 * We return the special error code of 2 to indicate that the
	 * certificates were removed.
	 */

	if (FILE_REMOVE == op) {
		if (certfile) {
			if (unlink(certfile) == -1 && errno != ENOENT) {
				warn("%s", certfile);
				goto out;
			} else
				dodbg("%s: unlinked", certfile);
		}

		if (chainfile) {
			if (unlink(chainfile) == -1 && errno != ENOENT) {
				warn("%s", chainfile);
				goto out;
			} else
				dodbg("%s: unlinked", chainfile);
		}

		if (fullchainfile) {
			if (unlink(fullchainfile) == -1 && errno != ENOENT) {
				warn("%s", fullchainfile);
				goto out;
			} else
				dodbg("%s: unlinked", fullchainfile);
		}

		rc = 2;
		goto out;
	}

	/*
	 * Start by downloading the chain PEM as a buffer.
	 * This is not NUL-terminated, but we're just going to guess
	 * that it's well-formed and not actually touch the data.
	 */
	if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL)
		goto out;

	if (chainfile) {
		if (!serialise(chainfile, ch, chsz, NULL, 0))
			goto out;

		dodbg("%s: created", chainfile);
	}

	/*
	 * Next, wait until we receive the DER encoded (signed)
	 * certificate from the network process.
	 * This comes as a stream of bytes: we don't know how many, so
	 * just keep downloading.
	 */

	if ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL)
		goto out;

	if (certfile) {
		if (!serialise(certfile, csr, csz, NULL, 0))
			goto out;

		dodbg("%s: created", certfile);
	}

	/*
	 * Finally, create the full-chain file.
	 * This is just the concatenation of the certificate and chain.
	 * We return the special error code 2 to indicate that the
	 * on-file certificates were changed.
	 */
	if (fullchainfile) {
		if (!serialise(fullchainfile, csr, csz, ch,
		    chsz))
			goto out;

		dodbg("%s: created", fullchainfile);
	}

	rc = 2;
out:
	close(certsock);
	free(csr);
	free(ch);
	return rc;
}