1 /*- 2 * Copyright (c) 2009,2010 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Alistair Crooks (agc@NetBSD.org) 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/socket.h> 33 #include <sys/stat.h> 34 #include <sys/select.h> 35 36 #include <netinet/in.h> 37 38 #include <errno.h> 39 #include <netdb.h> 40 #include <netpgp.h> 41 #include <regex.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "hkpd.h" 48 49 /* make the string have %xx -> %c */ 50 static size_t 51 frompercent(char *in, size_t insize, char *out, size_t outsize) 52 { 53 size_t outcc; 54 char *next; 55 char *pc; 56 57 outcc = 0; 58 for (pc = in ; (next = strchr(pc, '%')) != NULL ; pc = next + 3) { 59 (void) memcpy(&out[outcc], pc, (size_t)(next - pc)); 60 outcc += (size_t)(next - pc); 61 out[outcc++] = (char)strtol(next + 1, NULL, 16); 62 } 63 (void) memcpy(&out[outcc], pc, insize - (int)(pc - in)); 64 outcc += insize - (int)(pc - in); 65 out[outcc] = 0x0; 66 return outcc; 67 } 68 69 #define HKP_HTTP_LEVEL "HTTP/1.0" 70 #define HKP_NAME "hkpd" 71 #define HKP_MIME_GET "application/pgp-keys" 72 #define HKP_MIME_INDEX "text/plain" 73 #define HKP_MACHREAD "info:1:1\r\n" 74 75 #define HKP_SUCCESS 200 76 #define HKP_NOT_FOUND 404 77 78 /* make into html */ 79 static int 80 htmlify(char *buf, size_t size, const int code, const int get, const char *title, const char *out, const char *body) 81 { 82 return snprintf(buf, size, 83 "%s %d %s\r\n" 84 "Server: %s/%d\r\n" 85 "Content-type: %s\r\n" 86 "\r\n" 87 "%s" 88 "%s", 89 HKP_HTTP_LEVEL, code, (code == HKP_SUCCESS) ? "OK" : "not found", 90 HKP_NAME, HKPD_VERSION, 91 (get) ? HKP_MIME_GET : HKP_MIME_INDEX, 92 (get || strcmp(out, "mr") != 0) ? "" : HKP_MACHREAD, 93 body); 94 } 95 96 /* send the response now */ 97 static int 98 response(int sock, const int code, const char *search, const int get, char *buf, int cc, const char *out) 99 { 100 char outbuf[1024 * 512]; 101 char item[BUFSIZ]; 102 int tot; 103 int wc; 104 int n; 105 106 if (buf == NULL) { 107 (void) snprintf(item, sizeof(item), 108 "Error handling request: No keys found for '%s'\r\n", search); 109 n = htmlify(outbuf, sizeof(outbuf), code, get, 110 "Error handling request\r\n", 111 out, 112 item); 113 } else { 114 (void) snprintf(item, sizeof(item), "Search results for '%s'", search); 115 n = htmlify(outbuf, sizeof(outbuf), code, get, 116 item, 117 out, 118 buf); 119 } 120 for (tot = 0 ; (wc = write(sock, &outbuf[tot], n - tot)) > 0 && tot < n ; tot += wc) { 121 } 122 return 1; 123 } 124 125 /* get a socket (we'll bind it later) */ 126 static int 127 hkpd_sock_get(const int fam) 128 { 129 int sock; 130 int on = 1; 131 132 sock = socket((fam == 4) ? AF_INET : AF_INET6, SOCK_STREAM, 0); 133 if (sock < 0) { 134 (void) fprintf(stderr,"hkpd_sock_get: can't get a socket\n"); 135 return -1; 136 } 137 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 138 (void *)&on, sizeof(on)) == -1) { 139 (void) fprintf(stderr, 140 "hkpd_sock_get: can't set SO_REUSEADDR\n"); 141 return -1; 142 } 143 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, 144 (void *)&on, sizeof(on)) == -1) { 145 (void) fprintf(stderr, 146 "hkpd_sock_get: can't set SO_KEEPALIVE\n"); 147 return -1; 148 } 149 return sock; 150 } 151 152 /**************************************************************************/ 153 154 /* get a socket and bind it to the server */ 155 int 156 hkpd_sock_bind(const char *hostname, const int port, const int fam) 157 { 158 struct addrinfo hints; 159 struct addrinfo *res; 160 char portstr[32]; 161 int sock; 162 int rc = 0; 163 164 (void) memset(&hints, 0, sizeof(hints)); 165 hints.ai_family = (fam == 4) ? PF_INET : PF_INET6; 166 hints.ai_socktype = SOCK_STREAM; 167 (void) snprintf(portstr, sizeof(portstr), "%d", port); 168 /* Attempt connection */ 169 #ifdef AI_NUMERICSERV 170 hints.ai_flags = AI_NUMERICSERV; 171 #endif 172 if ((rc = getaddrinfo(hostname, portstr, &hints, &res)) != 0) { 173 hints.ai_flags = 0; 174 if ((rc = getaddrinfo(hostname, "hkp", &hints, &res)) != 0) { 175 (void) fprintf(stderr, "getaddrinfo: %s", 176 gai_strerror(rc)); 177 return -1; 178 } 179 } 180 if ((sock = hkpd_sock_get(fam)) < 0) { 181 (void) fprintf(stderr, "hkpd_sock_get failed %d\n", errno); 182 freeaddrinfo(res); 183 return -1; 184 } 185 if ((rc = bind(sock, res->ai_addr, res->ai_addrlen)) < 0) { 186 (void) fprintf(stderr, "bind failed %d\n", errno); 187 freeaddrinfo(res); 188 return -1; 189 } 190 freeaddrinfo(res); 191 if (rc < 0) { 192 (void) fprintf(stderr, "bind() to %s:%d failed (rc %d)\n", 193 hostname, port, rc); 194 } 195 return sock; 196 } 197 198 /* netpgp key daemon - does not return */ 199 int 200 hkpd(netpgp_t *netpgp, int sock4, int sock6) 201 { 202 struct sockaddr_in from; 203 regmatch_t searchmatches[10]; 204 regmatch_t opmatches[10]; 205 regmatch_t fmtmatch[3]; 206 socklen_t fromlen; 207 regex_t searchterm; 208 regex_t fmtterm; 209 regex_t opterm; 210 regex_t get; 211 fd_set sockets; 212 char search[BUFSIZ]; 213 char buf[BUFSIZ]; 214 char *cp; 215 char fmt[10]; 216 int newsock; 217 int sock; 218 int code; 219 int ok; 220 int cc; 221 int n; 222 223 /* GET /pks/lookup?search=agc%40netbsd.org&op=index&options=mr HTTP/1.1\n */ 224 #define HTTPGET "GET /pks/lookup\\?" 225 #define OPTERM "op=([a-zA-Z]+)" 226 #define SEARCHTERM "search=([^ \t&]+)" 227 #define FMT "options=(mr|json)" 228 229 (void) regcomp(&get, HTTPGET, REG_EXTENDED); 230 (void) regcomp(&opterm, OPTERM, REG_EXTENDED); 231 (void) regcomp(&searchterm, SEARCHTERM, REG_EXTENDED); 232 (void) regcomp(&fmtterm, FMT, REG_EXTENDED); 233 if (sock4 >= 0) { 234 listen(sock4, 32); 235 } 236 if (sock6 >= 0) { 237 listen(sock6, 32); 238 } 239 for (;;) { 240 /* find out which socket we have data on */ 241 FD_ZERO(&sockets); 242 if (sock4 >= 0) { 243 FD_SET(sock4, &sockets); 244 } 245 if (sock6 >= 0) { 246 FD_SET(sock6, &sockets); 247 } 248 if (select(32, &sockets, NULL, NULL, NULL) < 0) { 249 (void) fprintf(stderr, "bad select call\n"); 250 continue; 251 } 252 sock = (sock4 >= 0 && FD_ISSET(sock4, &sockets)) ? sock4 : sock6; 253 /* read data from socket */ 254 fromlen = sizeof(from); 255 newsock = accept(sock, (struct sockaddr *) &from, &fromlen); 256 cc = read(newsock, buf, sizeof(buf)); 257 /* parse the request */ 258 ok = 1; 259 if (regexec(&get, buf, 10, opmatches, 0) != 0) { 260 (void) fprintf(stderr, "not a valid get request\n"); 261 ok = 0; 262 } 263 if (ok && regexec(&opterm, buf, 10, opmatches, 0) != 0) { 264 (void) fprintf(stderr, "no operation in request\n"); 265 ok = 0; 266 } 267 if (ok && regexec(&fmtterm, buf, 3, fmtmatch, 0) == 0) { 268 (void) snprintf(fmt, sizeof(fmt), "%.*s", 269 (int)(fmtmatch[1].rm_eo - fmtmatch[1].rm_so), 270 &buf[(int)fmtmatch[1].rm_so]); 271 } else { 272 fmt[0] = 0x0; 273 } 274 if (ok && regexec(&searchterm, buf, 10, searchmatches, 0) != 0) { 275 (void) fprintf(stderr, "no search term in request\n"); 276 ok = 0; 277 } 278 if (!ok) { 279 (void) close(newsock); 280 continue; 281 } 282 /* convert from %2f to / etc */ 283 n = frompercent(&buf[searchmatches[1].rm_so], 284 (int)(searchmatches[1].rm_eo - searchmatches[1].rm_so), 285 search, 286 sizeof(search)); 287 code = HKP_NOT_FOUND; 288 cc = 0; 289 if (strncmp(&buf[opmatches[1].rm_so], "vindex", 6) == 0) { 290 cc = 0; 291 netpgp_setvar(netpgp, "subkey sigs", "yes"); 292 if (strcmp(fmt, "json") == 0) { 293 if (netpgp_match_keys_json(netpgp, &cp, search, "human", 1)) { 294 cc = strlen(cp); 295 code = HKP_SUCCESS; 296 } 297 } else if ((cp = netpgp_get_key(netpgp, search, fmt)) != NULL) { 298 cc = strlen(cp); 299 code = HKP_SUCCESS; 300 } 301 response(newsock, code, search, 0, cp, cc, fmt); 302 netpgp_unsetvar(netpgp, "subkey sigs"); 303 } else if (strncmp(&buf[opmatches[1].rm_so], "index", 5) == 0) { 304 cc = 0; 305 netpgp_unsetvar(netpgp, "subkey sigs"); 306 if (strcmp(fmt, "json") == 0) { 307 if (netpgp_match_keys_json(netpgp, &cp, search, "human", 0)) { 308 cc = strlen(cp); 309 code = HKP_SUCCESS; 310 } 311 } else if ((cp = netpgp_get_key(netpgp, search, fmt)) != NULL) { 312 cc = strlen(cp); 313 code = HKP_SUCCESS; 314 } 315 response(newsock, code, search, 0, cp, cc, fmt); 316 } else if (strncmp(&buf[opmatches[1].rm_so], "get", 3) == 0) { 317 if ((cp = netpgp_export_key(netpgp, search)) != NULL) { 318 cc = strlen(cp); 319 code = HKP_SUCCESS; 320 } 321 response(newsock, code, search, 1, cp, cc, fmt); 322 } 323 free(cp); 324 (void) close(newsock); 325 } 326 } 327