#include <assert.h>
#include <err.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern.h"
static int
tal_cmp(const void *a, const void *b)
{
char * const *sa = a;
char * const *sb = b;
return strcmp(*sa, *sb);
}
static struct tal *
tal_parse_buffer(const char *fn, char *buf, size_t len)
{
char *nl, *line, *f, *file = NULL;
unsigned char *der;
size_t dersz;
int rc = 0;
struct tal *tal = NULL;
EVP_PKEY *pkey = NULL;
int optcomment = 1;
if ((tal = calloc(1, sizeof(struct tal))) == NULL)
err(1, NULL);
while ((nl = memchr(buf, '\n', len)) != NULL) {
line = buf;
len -= nl + 1 - buf;
buf = nl + 1;
*nl = '\0';
if (nl > line && nl[-1] == '\r') {
nl[-1] = '\0';
nl--;
}
if (optcomment) {
if (line[0] == '#')
continue;
optcomment = 0;
}
if (*line == '\0')
break;
if (!valid_uri(line, nl - line, NULL)) {
warnx("%s: invalid URI", fn);
goto out;
}
if (!(strncasecmp(line, HTTPS_PROTO, HTTPS_PROTO_LEN) == 0 ||
strncasecmp(line, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0)) {
warnx("%s: unsupported URL schema: %s", fn, line);
goto out;
}
if (strcasecmp(nl - 4, ".cer")) {
warnx("%s: not a certificate URL: %s", fn, line);
goto out;
}
tal->uri = reallocarray(tal->uri,
tal->num_uris + 1, sizeof(char *));
if (tal->uri == NULL)
err(1, NULL);
tal->uri[tal->num_uris] = strdup(line);
if (tal->uri[tal->num_uris] == NULL)
err(1, NULL);
tal->num_uris++;
f = strrchr(line, '/') + 1;
if (file) {
if (strcmp(file, f)) {
warnx("%s: URL with different file name %s, "
"instead of %s", fn, f, file);
goto out;
}
} else
file = f;
}
if (tal->num_uris == 0) {
warnx("%s: no URIs in TAL file", fn);
goto out;
}
qsort(tal->uri, tal->num_uris, sizeof(tal->uri[0]), tal_cmp);
if ((base64_decode(buf, len, &der, &dersz)) == -1) {
warnx("%s: RFC 8630 section 2.1: subjectPublicKeyInfo: "
"bad public key", fn);
goto out;
}
tal->spki = der;
tal->spkisz = dersz;
pkey = d2i_PUBKEY(NULL, (const unsigned char **)&der, dersz);
if (pkey == NULL) {
warnx("%s: RFC 8630 section 2.1: subjectPublicKeyInfo: "
"failed public key parse", fn);
goto out;
}
if (der != tal->spki + tal->spkisz) {
warnx("%s: RFC 8630 section 2.1: subjectPublicKeyInfo: "
"%td bytes of trailing garbage", fn,
tal->spki + tal->spkisz - der);
}
rc = 1;
out:
if (rc == 0) {
tal_free(tal);
tal = NULL;
}
EVP_PKEY_free(pkey);
return tal;
}
struct tal *
tal_parse(const char *fn, char *buf, size_t len)
{
struct tal *p;
const char *d;
size_t dlen;
p = tal_parse_buffer(fn, buf, len);
if (p == NULL)
return NULL;
d = strrchr(fn, '/');
if (d == NULL)
d = fn;
else
d++;
dlen = strlen(d);
if (dlen > 4 && strcasecmp(d + dlen - 4, ".tal") == 0)
dlen -= 4;
if ((p->descr = strndup(d, dlen)) == NULL)
err(1, NULL);
return p;
}
void
tal_free(struct tal *p)
{
size_t i;
if (p == NULL)
return;
if (p->uri != NULL)
for (i = 0; i < p->num_uris; i++)
free(p->uri[i]);
free(p->spki);
free(p->uri);
free(p->descr);
free(p);
}
void
tal_buffer(struct ibuf *b, const struct tal *p)
{
size_t i;
io_simple_buffer(b, &p->id, sizeof(p->id));
io_buf_buffer(b, p->spki, p->spkisz);
io_str_buffer(b, p->descr);
io_simple_buffer(b, &p->num_uris, sizeof(p->num_uris));
for (i = 0; i < p->num_uris; i++)
io_str_buffer(b, p->uri[i]);
}
struct tal *
tal_read(struct ibuf *b)
{
size_t i;
struct tal *p;
if ((p = calloc(1, sizeof(struct tal))) == NULL)
err(1, NULL);
io_read_buf(b, &p->id, sizeof(p->id));
io_read_buf_alloc(b, (void **)&p->spki, &p->spkisz);
io_read_str(b, &p->descr);
io_read_buf(b, &p->num_uris, sizeof(p->num_uris));
if (p->spkisz <= 0 || p->num_uris <= 0)
errx(1, "tal_read: bad message");
if ((p->uri = calloc(p->num_uris, sizeof(char *))) == NULL)
err(1, NULL);
for (i = 0; i < p->num_uris; i++)
io_read_str(b, &p->uri[i]);
return p;
}