add an embryonic httpd key server (hkpd) to netpgp. this is based on the

discontinued draft for hkp servers, in lieu of any official rfc.

the post method for adding keys to the server has not been implemented.
not until i've gone through all of the implications, anyway.

hkp servers serve up pgp public keys over http. the default port is 11371,
but it can be any port, specifiable at run time. whilst this hkpd will
return valid html to browsers, it is aimed at the ''machine readable''
side, using the ''&options=mr'' option to the http get string.

this hkpd server will get hooked into the build at a later date.
This commit is contained in:
agc 2010-03-01 07:41:56 +00:00
parent 5e9539b84b
commit c854a26d0d
4 changed files with 463 additions and 0 deletions

View File

@ -0,0 +1,11 @@
# $NetBSD: Makefile,v 1.1 2010/03/01 07:41:56 agc Exp $
PROG=hkpd
SRCS=hkpd.c main.c
CPPFLAGS+=-g # -DHAVE_CONFIG_H=1
LDFLAGS+=-g
LDADD+= -lnetpgp
MKMAN=no
WARNS=0 # anything over 0 will fail at the link stage with IDEA errors
.include <bsd.prog.mk>

View File

@ -0,0 +1,275 @@
/*-
* Copyright (c) 2009,2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Alistair Crooks (agc@NetBSD.org)
*
* 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <netpgp.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "hkpd.h"
/* make the string have %xx -> %c */
static size_t
frompercent(char *in, size_t insize, char *out, size_t outsize)
{
size_t outcc;
char *next;
char *pc;
outcc = 0;
for (pc = in ; (next = strchr(pc, '%')) != NULL ; pc = next + 3) {
(void) memcpy(&out[outcc], pc, (size_t)(next - pc));
outcc += (size_t)(next - pc);
out[outcc++] = (char)strtol(next + 1, NULL, 16);
}
(void) memcpy(&out[outcc], pc, insize - (int)(pc - in));
outcc += insize - (int)(pc - in);
out[outcc] = 0x0;
return outcc;
}
#define HKP_HTTP_LEVEL "HTTP/1.0"
#define HKP_NAME "hkpd"
#define HKP_MIME_GET "application/pgp-keys"
#define HKP_MIME_INDEX "text/plain"
#define HKP_MACHREAD "info:1:1\r\n"
#define HKP_SUCCESS 200
#define HKP_NOT_FOUND 404
/* make into html */
static int
htmlify(char *buf, size_t size, const int code, const int get, const char *title, const int mr, const char *body)
{
return snprintf(buf, size,
"%s %d %s\r\n"
"Server: %s/%d\r\n"
"Content-type: %s\r\n"
"\r\n"
"%s"
"%s",
HKP_HTTP_LEVEL, code, (code == HKP_SUCCESS) ? "OK" : "not found",
HKP_NAME, HKPD_VERSION,
(get) ? HKP_MIME_GET : HKP_MIME_INDEX,
(get || !mr) ? "" : HKP_MACHREAD,
body);
}
/* send the response now */
static int
response(int sock, const int code, const char *search, const int get, char *buf, int cc, int mr)
{
char outbuf[BUFSIZ * 8];
char item[BUFSIZ];
int n;
if (buf == NULL) {
(void) snprintf(item, sizeof(item),
"Error handling request: No keys found for '%s'\r\n", search);
n = htmlify(outbuf, sizeof(outbuf), code, get,
"Error handling request\r\n",
mr,
item);
} else {
(void) snprintf(item, sizeof(item), "Search results for '%s'", search);
n = htmlify(outbuf, sizeof(outbuf), code, get,
item,
mr,
buf);
}
if (write(sock, outbuf, n) != n) {
(void) fprintf(stderr, "Short write\n");
}
return 1;
}
/**************************************************************************/
/* bind the socket to the server */
int
hkpd_sock_bind(int sock, const char *hostname, const int port)
{
struct addrinfo hints;
struct addrinfo *res;
char portstr[32];
int rc = 0;
(void) memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
(void) snprintf(portstr, sizeof(portstr), "%d", port);
/* Attempt connection */
#ifdef AI_NUMERICSERV
hints.ai_flags = AI_NUMERICSERV;
#endif
if ((rc = getaddrinfo(hostname, portstr, &hints, &res)) != 0) {
hints.ai_flags = 0;
if ((rc = getaddrinfo(hostname, "hkp", &hints, &res)) != 0) {
(void) fprintf(stderr, "getaddrinfo: %s",
gai_strerror(rc));
return -1;
}
}
if ((rc = bind(sock, res->ai_addr, res->ai_addrlen)) < 0) {
(void) fprintf(stderr, "bind failed %d\n", errno);
freeaddrinfo(res);
return -1;
}
freeaddrinfo(res);
if (rc < 0) {
(void) fprintf(stderr, "bind() to %s:%d failed (rc %d)\n",
hostname, port, rc);
}
return rc;
}
/* get a socket (we'll bind it later) */
int
hkpd_sock_get(void)
{
int sock;
int on = 1;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
(void) fprintf(stderr,"hkpd_sock_get: can't get a socket\n");
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(void *)&on, sizeof(on)) == -1) {
(void) fprintf(stderr,
"hkpd_sock_get: can't set SO_REUSEADDR\n");
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
(void *)&on, sizeof(on)) == -1) {
(void) fprintf(stderr,
"hkpd_sock_get: can't set SO_KEEPALIVE\n");
return -1;
}
return sock;
}
/* netpgp key daemon - does not return */
int
hkpd(netpgp_t *netpgp, int sock)
{
struct sockaddr_in from;
regmatch_t searchmatches[10];
regmatch_t opmatches[10];
regmatch_t mrmatch[3];
socklen_t fromlen;
regex_t machreadterm;
regex_t searchterm;
regex_t opterm;
regex_t get;
char search[BUFSIZ];
char buf[BUFSIZ];
char *cp;
int newsock;
int code;
int mr;
int ok;
int cc;
int n;
/* GET /pks/lookup?search=agc%40netbsd.org&op=index&options=mr HTTP/1.1\n */
#define HTTPGET "GET /pks/lookup\\?"
#define OPTERM "op=([a-zA-Z]+)"
#define SEARCHTERM "search=([^ \t&]+)"
#define MACHREAD "options=mr"
(void) regcomp(&get, HTTPGET, REG_EXTENDED);
(void) regcomp(&opterm, OPTERM, REG_EXTENDED);
(void) regcomp(&searchterm, SEARCHTERM, REG_EXTENDED);
(void) regcomp(&machreadterm, MACHREAD, REG_EXTENDED);
listen(sock, 32);
for (;;) {
/* read data from socket */
fromlen = sizeof(from);
newsock = accept(sock, (struct sockaddr *) &from, &fromlen);
cc = read(newsock, buf, sizeof(buf));
/* parse the request */
ok = 1;
if (regexec(&get, buf, 10, opmatches, 0) != 0) {
(void) fprintf(stderr, "not a valid get request\n");
ok = 0;
}
if (ok && regexec(&opterm, buf, 10, opmatches, 0) != 0) {
(void) fprintf(stderr, "no operation in request\n");
ok = 0;
}
if (ok) {
mr = (regexec(&machreadterm, buf, 3, mrmatch, 0) == 0);
}
if (ok && regexec(&searchterm, buf, 10, searchmatches, 0) != 0) {
(void) fprintf(stderr, "no search term in request\n");
ok = 0;
}
if (!ok) {
(void) close(newsock);
continue;
}
/* convert from %2f to / etc */
n = frompercent(&buf[searchmatches[1].rm_so],
(int)(searchmatches[1].rm_eo - searchmatches[1].rm_so),
search,
sizeof(search));
code = HKP_NOT_FOUND;
cc = 0;
if (strncmp(&buf[opmatches[1].rm_so], "vindex", 6) == 0 ||
strncmp(&buf[opmatches[1].rm_so], "index", 5) == 0) {
cc = 0;
if ((cp = netpgp_get_key(netpgp, search, (mr) ? "mr" : "")) != NULL) {
cc = strlen(cp);
code = HKP_SUCCESS;
}
response(newsock, code, search, 0, cp, cc, mr);
} else if (strncmp(&buf[opmatches[1].rm_so], "get", 3) == 0) {
if ((cp = netpgp_export_key(netpgp, search)) != NULL) {
cc = strlen(cp);
code = HKP_SUCCESS;
}
response(newsock, code, search, 1, cp, cc, mr);
}
free(cp);
(void) close(newsock);
}
}

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2009,2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Alistair Crooks (agc@NetBSD.org)
*
* 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
#ifndef HKPD_H_
#define HKPD_H_ 20091022
#include <netpgp.h>
#define HKPD_VERSION 20091022
int hkpd_sock_bind(int, const char *, const int);
int hkpd_sock_get(void);
int hkpd(netpgp_t *, int);
#endif

View File

@ -0,0 +1,137 @@
/*-
* Copyright (c) 2009,2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Alistair Crooks (agc@NetBSD.org)
*
* 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "hkpd.h"
#define HAVE_DAEMON 1
/* set the home directory value to "home/subdir" */
static int
set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
{
struct stat st;
char d[MAXPATHLEN];
if (home == NULL) {
if (!quiet) {
(void) fprintf(stderr, "NULL HOME directory\n");
}
return 0;
}
(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
if (stat(d, &st) == 0) {
if ((st.st_mode & S_IFMT) == S_IFDIR) {
netpgp_setvar(netpgp, "homedir", d);
return 1;
}
(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
d);
return 0;
}
if (!quiet) {
(void) fprintf(stderr,
"netpgp: warning homedir \"%s\" not found\n", d);
}
return 1;
}
int
main(int argc, char **argv)
{
netpgp_t netpgp;
char *host;
int daemonise;
int port;
int sock;
int i;
(void) memset(&netpgp, 0x0, sizeof(netpgp));
/* set some defaults */
set_homedir(&netpgp, getenv("HOME"), "/.gnupg", 1);
port = 11371;
host = strdup("localhost");
daemonise = 1;
while ((i = getopt(argc, argv, "DH:Vh:p:v:")) != -1) {
switch(i) {
case 'D':
daemonise = 0;
break;
case 'H':
set_homedir(&netpgp, optarg, NULL, 0);
break;
case 'V':
printf("%s: Version %d\n", *argv, HKPD_VERSION);
exit(EXIT_SUCCESS);
case 'h':
(void) free(host);
host = strdup(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'v':
netpgp_set_debug(optarg);
break;
default:
break;
}
}
#ifdef HAVE_DAEMON
/* if we are supposed to be a daemon, detach from controlling tty */
if (daemonise && daemon(0, 0) < 0) {
(void) fprintf(stderr, "daemon() failed\n");
exit(EXIT_FAILURE);
}
#endif
if (!netpgp_init(&netpgp)) {
(void) fprintf(stderr, "can't initialise\n");
exit(EXIT_FAILURE);
}
if ((sock = hkpd_sock_get()) < 0) {
(void) fprintf(stderr,"hkpd: can't get a socket\n");
exit(EXIT_FAILURE);
}
if (hkpd_sock_bind(sock, host, port) < 0) {
(void) fprintf(stderr,"hkpd: can't connect socket\n");
exit(EXIT_FAILURE);
}
hkpd(&netpgp, sock);
}