744 lines
16 KiB
C
744 lines
16 KiB
C
/* $OpenBSD: filemode.c,v 1.40 2024/03/22 03:38:12 job Exp $ */
|
|
/*
|
|
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
|
|
* Copyright (c) 2019 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 AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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/queue.h>
|
|
#include <sys/tree.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <imsg.h>
|
|
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
#include "extern.h"
|
|
#include "json.h"
|
|
|
|
extern int verbose;
|
|
|
|
static X509_STORE_CTX *ctx;
|
|
static struct auth_tree auths = RB_INITIALIZER(&auths);
|
|
static struct crl_tree crlt = RB_INITIALIZER(&crlt);
|
|
|
|
struct tal *talobj[TALSZ_MAX];
|
|
|
|
/*
|
|
* Use the X509 CRL Distribution Points to locate the CRL needed for
|
|
* verification.
|
|
*/
|
|
static void
|
|
parse_load_crl(char *uri)
|
|
{
|
|
struct crl *crl;
|
|
char *f;
|
|
size_t flen;
|
|
|
|
if (uri == NULL)
|
|
return;
|
|
if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
|
|
warnx("bad CRL distribution point URI %s", uri);
|
|
return;
|
|
}
|
|
uri += RSYNC_PROTO_LEN;
|
|
|
|
f = load_file(uri, &flen);
|
|
if (f == NULL) {
|
|
warn("parse file %s", uri);
|
|
return;
|
|
}
|
|
|
|
crl = crl_parse(uri, f, flen);
|
|
if (crl != NULL && !crl_insert(&crlt, crl))
|
|
crl_free(crl);
|
|
|
|
free(f);
|
|
}
|
|
|
|
/*
|
|
* Parse the cert pointed at by the AIA URI while doing that also load
|
|
* the CRL of this cert. While the CRL is validated the returned cert
|
|
* is not. The caller needs to make sure it is validated once all
|
|
* necessary certs were loaded. Returns NULL on failure.
|
|
*/
|
|
static struct cert *
|
|
parse_load_cert(char *uri)
|
|
{
|
|
struct cert *cert = NULL;
|
|
char *f;
|
|
size_t flen;
|
|
|
|
if (uri == NULL)
|
|
return NULL;
|
|
|
|
if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
|
|
warnx("bad authority information access URI %s", uri);
|
|
return NULL;
|
|
}
|
|
uri += RSYNC_PROTO_LEN;
|
|
|
|
f = load_file(uri, &flen);
|
|
if (f == NULL) {
|
|
warn("parse file %s", uri);
|
|
goto done;
|
|
}
|
|
|
|
cert = cert_parse_pre(uri, f, flen);
|
|
free(f);
|
|
|
|
if (cert == NULL)
|
|
goto done;
|
|
if (cert->purpose != CERT_PURPOSE_CA) {
|
|
warnx("AIA reference to bgpsec cert %s", uri);
|
|
goto done;
|
|
}
|
|
/* try to load the CRL of this cert */
|
|
parse_load_crl(cert->crl);
|
|
|
|
return cert;
|
|
|
|
done:
|
|
cert_free(cert);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Build the certificate chain by using the Authority Information Access.
|
|
* This requires that the TA are already validated and added to the auths
|
|
* tree. Once the TA is located in the chain the chain is validated in
|
|
* reverse order.
|
|
*/
|
|
static void
|
|
parse_load_certchain(char *uri)
|
|
{
|
|
struct cert *stack[MAX_CERT_DEPTH] = { 0 };
|
|
char *filestack[MAX_CERT_DEPTH];
|
|
struct cert *cert;
|
|
struct crl *crl;
|
|
struct auth *a;
|
|
const char *errstr;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_CERT_DEPTH; i++) {
|
|
filestack[i] = uri;
|
|
stack[i] = cert = parse_load_cert(uri);
|
|
if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) {
|
|
warnx("failed to build authority chain");
|
|
goto fail;
|
|
}
|
|
if (auth_find(&auths, cert->ski) != NULL) {
|
|
assert(i == 0);
|
|
goto fail;
|
|
}
|
|
if ((a = auth_find(&auths, cert->aki)) != NULL)
|
|
break; /* found chain to TA */
|
|
uri = cert->aia;
|
|
}
|
|
|
|
if (i >= MAX_CERT_DEPTH) {
|
|
warnx("authority chain exceeds max depth of %d",
|
|
MAX_CERT_DEPTH);
|
|
goto fail;
|
|
}
|
|
|
|
/* TA found play back the stack and add all certs */
|
|
for (; i >= 0; i--) {
|
|
cert = stack[i];
|
|
uri = filestack[i];
|
|
|
|
crl = crl_get(&crlt, a);
|
|
if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) ||
|
|
!valid_cert(uri, a, cert)) {
|
|
if (errstr != NULL)
|
|
warnx("%s: %s", uri, errstr);
|
|
goto fail;
|
|
}
|
|
cert->talid = a->cert->talid;
|
|
a = auth_insert(&auths, cert, a);
|
|
stack[i] = NULL;
|
|
}
|
|
|
|
return;
|
|
fail:
|
|
for (i = 0; i < MAX_CERT_DEPTH; i++)
|
|
cert_free(stack[i]);
|
|
}
|
|
|
|
static void
|
|
parse_load_ta(struct tal *tal)
|
|
{
|
|
const char *filename;
|
|
struct cert *cert;
|
|
unsigned char *f = NULL;
|
|
char *file;
|
|
size_t flen;
|
|
|
|
/* does not matter which URI, all end with same filename */
|
|
filename = strrchr(tal->uri[0], '/');
|
|
assert(filename);
|
|
|
|
if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1)
|
|
err(1, NULL);
|
|
|
|
f = load_file(file, &flen);
|
|
if (f == NULL) {
|
|
warn("parse file %s", file);
|
|
goto out;
|
|
}
|
|
|
|
/* Extract certificate data. */
|
|
cert = cert_parse_pre(file, f, flen);
|
|
cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
|
|
if (cert == NULL)
|
|
goto out;
|
|
|
|
cert->talid = tal->id;
|
|
|
|
if (!valid_ta(file, &auths, cert))
|
|
cert_free(cert);
|
|
else
|
|
auth_insert(&auths, cert, NULL);
|
|
out:
|
|
free(file);
|
|
free(f);
|
|
}
|
|
|
|
static struct tal *
|
|
find_tal(struct cert *cert)
|
|
{
|
|
EVP_PKEY *pk, *opk;
|
|
struct tal *tal;
|
|
int i;
|
|
|
|
if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; i < TALSZ_MAX; i++) {
|
|
const unsigned char *pkey;
|
|
|
|
if (talobj[i] == NULL)
|
|
break;
|
|
tal = talobj[i];
|
|
pkey = tal->pkey;
|
|
pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
|
|
if (pk == NULL)
|
|
continue;
|
|
if (EVP_PKEY_cmp(pk, opk) == 1) {
|
|
EVP_PKEY_free(pk);
|
|
return tal;
|
|
}
|
|
EVP_PKEY_free(pk);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
print_signature_path(const char *crl, const char *aia, const struct auth *a)
|
|
{
|
|
if (crl != NULL)
|
|
printf("Signature path: %s\n", crl);
|
|
if (a->cert->mft != NULL)
|
|
printf(" %s\n", a->cert->mft);
|
|
if (aia != NULL)
|
|
printf(" %s\n", aia);
|
|
|
|
for (; a != NULL; a = a->issuer) {
|
|
if (a->cert->crl != NULL)
|
|
printf(" %s\n", a->cert->crl);
|
|
if (a->issuer != NULL && a->issuer->cert != NULL &&
|
|
a->issuer->cert->mft != NULL)
|
|
printf(" %s\n",
|
|
a->issuer->cert->mft);
|
|
if (a->cert->aia != NULL)
|
|
printf(" %s\n", a->cert->aia);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse file passed with -f option.
|
|
*/
|
|
static void
|
|
proc_parser_file(char *file, unsigned char *buf, size_t len)
|
|
{
|
|
static int num;
|
|
X509 *x509 = NULL;
|
|
struct aspa *aspa = NULL;
|
|
struct cert *cert = NULL;
|
|
struct crl *crl = NULL;
|
|
struct gbr *gbr = NULL;
|
|
struct geofeed *geofeed = NULL;
|
|
struct mft *mft = NULL;
|
|
struct roa *roa = NULL;
|
|
struct rsc *rsc = NULL;
|
|
struct spl *spl = NULL;
|
|
struct tak *tak = NULL;
|
|
struct tal *tal = NULL;
|
|
char *aia = NULL, *aki = NULL;
|
|
char *crl_uri = NULL;
|
|
time_t *expires = NULL, *notafter = NULL;
|
|
struct auth *a;
|
|
struct crl *c;
|
|
const char *errstr = NULL, *valid;
|
|
int status = 0;
|
|
char filehash[SHA256_DIGEST_LENGTH];
|
|
char *hash;
|
|
enum rtype type;
|
|
int is_ta = 0;
|
|
|
|
if (outformats & FORMAT_JSON) {
|
|
json_do_start(stdout);
|
|
} else {
|
|
if (num++ > 0)
|
|
printf("--\n");
|
|
}
|
|
|
|
if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
|
|
file += RSYNC_PROTO_LEN;
|
|
buf = load_file(file, &len);
|
|
if (buf == NULL) {
|
|
warn("parse file %s", file);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
|
|
errx(1, "EVP_Digest failed in %s", __func__);
|
|
|
|
if (base64_encode(filehash, sizeof(filehash), &hash) == -1)
|
|
errx(1, "base64_encode failed in %s", __func__);
|
|
|
|
if (outformats & FORMAT_JSON) {
|
|
json_do_string("file", file);
|
|
json_do_string("hash_id", hash);
|
|
} else {
|
|
printf("File: %s\n", file);
|
|
printf("Hash identifier: %s\n", hash);
|
|
}
|
|
|
|
free(hash);
|
|
|
|
type = rtype_from_file_extension(file);
|
|
|
|
switch (type) {
|
|
case RTYPE_ASPA:
|
|
aspa = aspa_parse(&x509, file, -1, buf, len);
|
|
if (aspa == NULL)
|
|
break;
|
|
aia = aspa->aia;
|
|
aki = aspa->aki;
|
|
expires = &aspa->expires;
|
|
notafter = &aspa->notafter;
|
|
break;
|
|
case RTYPE_CER:
|
|
cert = cert_parse_pre(file, buf, len);
|
|
if (cert == NULL)
|
|
break;
|
|
is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS;
|
|
if (!is_ta)
|
|
cert = cert_parse(file, cert);
|
|
if (cert == NULL)
|
|
break;
|
|
aia = cert->aia;
|
|
aki = cert->aki;
|
|
x509 = cert->x509;
|
|
if (X509_up_ref(x509) == 0)
|
|
errx(1, "%s: X509_up_ref failed", __func__);
|
|
expires = &cert->expires;
|
|
notafter = &cert->notafter;
|
|
break;
|
|
case RTYPE_CRL:
|
|
crl = crl_parse(file, buf, len);
|
|
if (crl == NULL)
|
|
break;
|
|
crl_print(crl);
|
|
break;
|
|
case RTYPE_MFT:
|
|
mft = mft_parse(&x509, file, -1, buf, len);
|
|
if (mft == NULL)
|
|
break;
|
|
aia = mft->aia;
|
|
aki = mft->aki;
|
|
expires = &mft->expires;
|
|
notafter = &mft->nextupdate;
|
|
break;
|
|
case RTYPE_GBR:
|
|
gbr = gbr_parse(&x509, file, -1, buf, len);
|
|
if (gbr == NULL)
|
|
break;
|
|
aia = gbr->aia;
|
|
aki = gbr->aki;
|
|
expires = &gbr->expires;
|
|
notafter = &gbr->notafter;
|
|
break;
|
|
case RTYPE_GEOFEED:
|
|
geofeed = geofeed_parse(&x509, file, -1, buf, len);
|
|
if (geofeed == NULL)
|
|
break;
|
|
aia = geofeed->aia;
|
|
aki = geofeed->aki;
|
|
expires = &geofeed->expires;
|
|
notafter = &geofeed->notafter;
|
|
break;
|
|
case RTYPE_ROA:
|
|
roa = roa_parse(&x509, file, -1, buf, len);
|
|
if (roa == NULL)
|
|
break;
|
|
aia = roa->aia;
|
|
aki = roa->aki;
|
|
expires = &roa->expires;
|
|
notafter = &roa->notafter;
|
|
break;
|
|
case RTYPE_RSC:
|
|
rsc = rsc_parse(&x509, file, -1, buf, len);
|
|
if (rsc == NULL)
|
|
break;
|
|
aia = rsc->aia;
|
|
aki = rsc->aki;
|
|
expires = &rsc->expires;
|
|
notafter = &rsc->notafter;
|
|
break;
|
|
case RTYPE_SPL:
|
|
spl = spl_parse(&x509, file, -1, buf, len);
|
|
if (spl == NULL)
|
|
break;
|
|
aia = spl->aia;
|
|
aki = spl->aki;
|
|
expires = &spl->expires;
|
|
notafter = &spl->notafter;
|
|
break;
|
|
case RTYPE_TAK:
|
|
tak = tak_parse(&x509, file, -1, buf, len);
|
|
if (tak == NULL)
|
|
break;
|
|
aia = tak->aia;
|
|
aki = tak->aki;
|
|
expires = &tak->expires;
|
|
notafter = &tak->notafter;
|
|
break;
|
|
case RTYPE_TAL:
|
|
tal = tal_parse(file, buf, len);
|
|
if (tal == NULL)
|
|
break;
|
|
tal_print(tal);
|
|
break;
|
|
default:
|
|
printf("%s: unsupported file type\n", file);
|
|
break;
|
|
}
|
|
|
|
if (aia != NULL) {
|
|
x509_get_crl(x509, file, &crl_uri);
|
|
parse_load_crl(crl_uri);
|
|
if (auth_find(&auths, aki) == NULL)
|
|
parse_load_certchain(aia);
|
|
a = auth_find(&auths, aki);
|
|
c = crl_get(&crlt, a);
|
|
|
|
if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) {
|
|
switch (type) {
|
|
case RTYPE_ASPA:
|
|
status = aspa->valid;
|
|
break;
|
|
case RTYPE_GEOFEED:
|
|
status = geofeed->valid;
|
|
break;
|
|
case RTYPE_ROA:
|
|
status = roa->valid;
|
|
break;
|
|
case RTYPE_RSC:
|
|
status = rsc->valid;
|
|
break;
|
|
case RTYPE_SPL:
|
|
status = spl->valid;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (status && cert == NULL) {
|
|
struct cert *eecert;
|
|
|
|
eecert = cert_parse_ee_cert(file, a->cert->talid, x509);
|
|
if (eecert == NULL)
|
|
status = 0;
|
|
cert_free(eecert);
|
|
} else if (status) {
|
|
cert->talid = a->cert->talid;
|
|
constraints_validate(file, cert);
|
|
}
|
|
} else if (is_ta) {
|
|
if ((tal = find_tal(cert)) != NULL) {
|
|
cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
|
|
status = (cert != NULL);
|
|
if (outformats & FORMAT_JSON)
|
|
json_do_string("tal", tal->descr);
|
|
else
|
|
printf("TAL: %s\n",
|
|
tal->descr);
|
|
tal = NULL;
|
|
} else {
|
|
cert_free(cert);
|
|
cert = NULL;
|
|
expires = NULL;
|
|
status = 0;
|
|
}
|
|
}
|
|
|
|
if (expires != NULL) {
|
|
if (status && aia != NULL)
|
|
*expires = x509_find_expires(*notafter, a, &crlt);
|
|
|
|
switch (type) {
|
|
case RTYPE_ASPA:
|
|
aspa_print(x509, aspa);
|
|
break;
|
|
case RTYPE_CER:
|
|
cert_print(cert);
|
|
break;
|
|
case RTYPE_GBR:
|
|
gbr_print(x509, gbr);
|
|
break;
|
|
case RTYPE_GEOFEED:
|
|
geofeed_print(x509, geofeed);
|
|
break;
|
|
case RTYPE_MFT:
|
|
mft_print(x509, mft);
|
|
break;
|
|
case RTYPE_ROA:
|
|
roa_print(x509, roa);
|
|
break;
|
|
case RTYPE_RSC:
|
|
rsc_print(x509, rsc);
|
|
break;
|
|
case RTYPE_SPL:
|
|
spl_print(x509, spl);
|
|
break;
|
|
case RTYPE_TAK:
|
|
tak_print(x509, tak);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status)
|
|
valid = "OK";
|
|
else if (aia == NULL)
|
|
valid = "N/A";
|
|
else
|
|
valid = "Failed";
|
|
|
|
if (outformats & FORMAT_JSON) {
|
|
json_do_string("validation", valid);
|
|
if (errstr != NULL)
|
|
json_do_string("error", errstr);
|
|
} else {
|
|
printf("Validation: %s", valid);
|
|
if (errstr != NULL)
|
|
printf(", %s", errstr);
|
|
}
|
|
|
|
if (outformats & FORMAT_JSON)
|
|
json_do_finish();
|
|
else {
|
|
printf("\n");
|
|
|
|
if (status && aia != NULL) {
|
|
print_signature_path(crl_uri, aia, a);
|
|
if (expires != NULL)
|
|
printf("Signature path expires: %s\n",
|
|
time2str(*expires));
|
|
}
|
|
|
|
if (x509 == NULL)
|
|
goto out;
|
|
if (type == RTYPE_TAL || type == RTYPE_CRL)
|
|
goto out;
|
|
|
|
if (verbose) {
|
|
if (!X509_print_fp(stdout, x509))
|
|
errx(1, "X509_print_fp");
|
|
}
|
|
|
|
if (verbose > 1) {
|
|
if (!PEM_write_X509(stdout, x509))
|
|
errx(1, "PEM_write_X509");
|
|
}
|
|
}
|
|
|
|
out:
|
|
free(crl_uri);
|
|
X509_free(x509);
|
|
aspa_free(aspa);
|
|
cert_free(cert);
|
|
crl_free(crl);
|
|
gbr_free(gbr);
|
|
geofeed_free(geofeed);
|
|
mft_free(mft);
|
|
roa_free(roa);
|
|
rsc_free(rsc);
|
|
tak_free(tak);
|
|
tal_free(tal);
|
|
}
|
|
|
|
/*
|
|
* Process a file request, in general don't send anything back.
|
|
*/
|
|
static void
|
|
parse_file(struct entityq *q, struct msgbuf *msgq)
|
|
{
|
|
struct entity *entp;
|
|
struct ibuf *b;
|
|
struct tal *tal;
|
|
time_t dummy = 0;
|
|
|
|
while ((entp = TAILQ_FIRST(q)) != NULL) {
|
|
TAILQ_REMOVE(q, entp, entries);
|
|
|
|
switch (entp->type) {
|
|
case RTYPE_FILE:
|
|
proc_parser_file(entp->file, entp->data, entp->datasz);
|
|
break;
|
|
case RTYPE_TAL:
|
|
if ((tal = tal_parse(entp->file, entp->data,
|
|
entp->datasz)) == NULL)
|
|
errx(1, "%s: could not parse tal file",
|
|
entp->file);
|
|
tal->id = entp->talid;
|
|
talobj[tal->id] = tal;
|
|
parse_load_ta(tal);
|
|
break;
|
|
default:
|
|
errx(1, "unhandled entity type %d", entp->type);
|
|
}
|
|
|
|
b = io_new_buffer();
|
|
io_simple_buffer(b, &entp->type, sizeof(entp->type));
|
|
io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
|
|
io_simple_buffer(b, &entp->talid, sizeof(entp->talid));
|
|
io_str_buffer(b, entp->file);
|
|
io_simple_buffer(b, &dummy, sizeof(dummy));
|
|
io_close_buffer(msgq, b);
|
|
entity_free(entp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process responsible for parsing and validating content.
|
|
* All this process does is wait to be told about a file to parse, then
|
|
* it parses it and makes sure that the data being returned is fully
|
|
* validated and verified.
|
|
* The process will exit cleanly only when fd is closed.
|
|
*/
|
|
void
|
|
proc_filemode(int fd)
|
|
{
|
|
struct entityq q;
|
|
struct msgbuf msgq;
|
|
struct pollfd pfd;
|
|
struct entity *entp;
|
|
struct ibuf *b, *inbuf = NULL;
|
|
|
|
/* Only allow access to the cache directory. */
|
|
if (unveil(".", "r") == -1)
|
|
err(1, "unveil cachedir");
|
|
if (pledge("stdio rpath", NULL) == -1)
|
|
err(1, "pledge");
|
|
|
|
ERR_load_crypto_strings();
|
|
OpenSSL_add_all_ciphers();
|
|
OpenSSL_add_all_digests();
|
|
x509_init_oid();
|
|
constraints_parse();
|
|
|
|
if ((ctx = X509_STORE_CTX_new()) == NULL)
|
|
err(1, "X509_STORE_CTX_new");
|
|
TAILQ_INIT(&q);
|
|
|
|
msgbuf_init(&msgq);
|
|
msgq.fd = fd;
|
|
|
|
pfd.fd = fd;
|
|
|
|
for (;;) {
|
|
pfd.events = POLLIN;
|
|
if (msgq.queued)
|
|
pfd.events |= POLLOUT;
|
|
|
|
if (poll(&pfd, 1, INFTIM) == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
err(1, "poll");
|
|
}
|
|
if ((pfd.revents & (POLLERR|POLLNVAL)))
|
|
errx(1, "poll: bad descriptor");
|
|
|
|
/* If the parent closes, return immediately. */
|
|
|
|
if ((pfd.revents & POLLHUP))
|
|
break;
|
|
|
|
if ((pfd.revents & POLLIN)) {
|
|
b = io_buf_read(fd, &inbuf);
|
|
if (b != NULL) {
|
|
entp = calloc(1, sizeof(struct entity));
|
|
if (entp == NULL)
|
|
err(1, NULL);
|
|
entity_read_req(b, entp);
|
|
TAILQ_INSERT_TAIL(&q, entp, entries);
|
|
ibuf_free(b);
|
|
}
|
|
}
|
|
|
|
if (pfd.revents & POLLOUT) {
|
|
switch (msgbuf_write(&msgq)) {
|
|
case 0:
|
|
errx(1, "write: connection closed");
|
|
case -1:
|
|
err(1, "write");
|
|
}
|
|
}
|
|
|
|
parse_file(&q, &msgq);
|
|
}
|
|
|
|
msgbuf_clear(&msgq);
|
|
while ((entp = TAILQ_FIRST(&q)) != NULL) {
|
|
TAILQ_REMOVE(&q, entp, entries);
|
|
entity_free(entp);
|
|
}
|
|
|
|
auth_tree_free(&auths);
|
|
crl_tree_free(&crlt);
|
|
|
|
X509_STORE_CTX_free(ctx);
|
|
ibuf_free(inbuf);
|
|
|
|
exit(0);
|
|
}
|