]> the.earth.li Git - onak.git/blob - onak.c
Extend database backends to support fetching by key fingerprint
[onak.git] / onak.c
1 /*
2  * onak.c - An OpenPGP keyserver.
3  *
4  * This is the main swiss army knife binary.
5  *
6  * Copyright 2002 Jonathan McDowell <noodles@earth.li>
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the Free
10  * Software Foundation; version 2 of the License.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 51
19  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #include "armor.h"
32 #include "charfuncs.h"
33 #include "cleankey.h"
34 #include "cleanup.h"
35 #include "keydb.h"
36 #include "keyid.h"
37 #include "keyindex.h"
38 #include "keystructs.h"
39 #include "log.h"
40 #include "mem.h"
41 #include "merge.h"
42 #include "onak-conf.h"
43 #include "parsekey.h"
44 #include "photoid.h"
45 #include "version.h"
46
47 void find_keys(char *search, uint64_t keyid, uint8_t *fp, bool ishex,
48                 bool isfp, bool fingerprint, bool skshash, bool exact,
49                 bool verbose)
50 {
51         struct openpgp_publickey *publickey = NULL;
52         int count = 0;
53
54         if (ishex) {
55                 count = config.dbbackend->fetch_key_id(keyid, &publickey,
56                                 false);
57         } else if (isfp) {
58                 count = config.dbbackend->fetch_key_fp(fp, MAX_FINGERPRINT_LEN,
59                                 &publickey, false);
60         } else {
61                 count = config.dbbackend->fetch_key_text(search, &publickey);
62         }
63         if (publickey != NULL) {
64                 key_index(publickey, verbose, fingerprint, skshash, false);
65                 free_publickey(publickey);
66         } else if (count == 0) {
67                 puts("Key not found.");
68         } else {
69                 printf("Found %d keys, but maximum number to return is %d.\n",
70                                 count,
71                                 config.maxkeys);
72                 puts("Try again with a more specific search.");
73         }
74 }
75
76 /**
77  * @brief Context for the keyserver dumping function
78  */
79 struct dump_ctx {
80         /** Keys we've dumped so far to this file */
81         int count;
82         /** Maximum keys to dump per file */
83         int maxcount;
84         /** File descriptor for the current dump file */
85         int fd;
86         /** Number of the current dump file */
87         int filenum;
88         /** Base filename to use for dump files */
89         char *filebase;
90 };
91
92 void dump_func(void *ctx, struct openpgp_publickey *key)
93 {
94         struct openpgp_packet_list *packets = NULL;
95         struct openpgp_packet_list *list_end = NULL;
96         struct dump_ctx *state;
97         char filename[1024];
98
99         state = (struct dump_ctx *) ctx;
100
101         if (state->fd == -1 || state->count++ > state->maxcount) {
102                 if (state->fd != -1) {
103                         close(state->fd);
104                         state->fd = -1;
105                 }
106                 snprintf(filename, 1023, state->filebase, state->filenum);
107                 state->fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
108                 state->filenum++;
109                 state->count = 0;
110         }
111         flatten_publickey(key, &packets, &list_end);
112         write_openpgp_stream(file_putchar, &state->fd, packets);
113         free_packet_list(packets);
114         packets = list_end = NULL;
115
116         return;
117 }
118
119 static uint8_t hex2bin(char c)
120 {
121         if (c >= '0' && c <= '9') {
122                 return (c - '0');
123         } else if (c >= 'a' && c <= 'f') {
124                 return (c - 'a' + 10);
125         } else if (c >= 'A' && c <= 'F') {
126                 return (c - 'A' + 10);
127         }
128
129         return 255;
130 }
131
132 void usage(void) {
133         puts("onak " ONAK_VERSION " - an OpenPGP keyserver.\n");
134         puts("Usage:\n");
135         puts("\tonak [options] <command> <parameters>\n");
136         puts("\tCommands:\n");
137         puts("\tadd      - read armored OpenPGP keys from stdin and add to the"
138                 " keyserver");
139         puts("\tclean    - read armored OpenPGP keys from stdin, run the"
140                 " cleaning\n\t             routines against them and dump to"
141                 " stdout");
142         puts("\tdelete   - delete a given key from the keyserver");
143         puts("\tdump     - dump all the keys from the keyserver to a file or"
144                 " files\n\t           starting keydump*");
145         puts("\tget      - retrieves the key requested from the keyserver");
146         puts("\tgetphoto - retrieves the first photoid on the given key and"
147                 " dumps to\n\t           stdout");
148         puts("\tindex    - search for a key and list it");
149         puts("\tvindex   - search for a key and list it and its signatures");
150 }
151
152 int main(int argc, char *argv[])
153 {
154         struct openpgp_packet_list      *packets = NULL;
155         struct openpgp_packet_list      *list_end = NULL;
156         struct openpgp_publickey        *keys = NULL;
157         char                            *configfile = NULL;
158         int                              rc = EXIT_SUCCESS;
159         int                              result = 0;
160         char                            *search = NULL;
161         char                            *end = NULL;
162         uint64_t                         keyid = 0;
163         uint8_t                          fp[MAX_FINGERPRINT_LEN];
164         int                              i;
165         bool                             ishex = false;
166         bool                             isfp = false;
167         bool                             verbose = false;
168         bool                             update = false;
169         bool                             binary = false;
170         bool                             fingerprint = false;
171         bool                             skshash = false;
172         int                              optchar;
173         struct dump_ctx                  dumpstate;
174         struct skshash                   hash;
175
176         while ((optchar = getopt(argc, argv, "bc:fsuv")) != -1 ) {
177                 switch (optchar) {
178                 case 'b': 
179                         binary = true;
180                         break;
181                 case 'c':
182                         configfile = strdup(optarg);
183                         break;
184                 case 'f': 
185                         fingerprint = true;
186                         break;
187                 case 's': 
188                         skshash = true;
189                         break;
190                 case 'u': 
191                         update = true;
192                         break;
193                 case 'v': 
194                         verbose = true;
195                         setlogthreshold(LOGTHING_INFO);
196                         break;
197                 }
198         }
199
200         readconfig(configfile);
201         initlogthing("onak", config.logfile);
202         catchsignals();
203
204         if ((argc - optind) < 1) {
205                 usage();
206         } else if (!strcmp("dump", argv[optind])) {
207                 config.dbbackend->initdb(true);
208                 dumpstate.count = dumpstate.filenum = 0;
209                 dumpstate.maxcount = 100000;
210                 dumpstate.fd = -1;
211                 dumpstate.filebase = "keydump.%d.pgp";
212                 config.dbbackend->iterate_keys(dump_func, &dumpstate);
213                 if (dumpstate.fd != -1) {
214                         close(dumpstate.fd);
215                         dumpstate.fd = -1;
216                 }
217                 config.dbbackend->cleanupdb();
218         } else if (!strcmp("add", argv[optind])) {
219                 if (binary) {
220                         result = read_openpgp_stream(stdin_getchar, NULL,
221                                  &packets, 0);
222                         logthing(LOGTHING_INFO,
223                                         "read_openpgp_stream: %d", result);
224                 } else {
225                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
226                 }
227                 if (packets != NULL) {
228                         result = parse_keys(packets, &keys);
229                         free_packet_list(packets);
230                         packets = NULL;
231                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
232                                         result);
233
234                         result = cleankeys(keys);
235                         logthing(LOGTHING_INFO, "%d keys cleaned.",
236                                         result);
237
238                         config.dbbackend->initdb(false);
239                         logthing(LOGTHING_NOTICE, "Got %d new keys.",
240                                         config.dbbackend->update_keys(&keys,
241                                         false));
242                         if (keys != NULL && update) {
243                                 flatten_publickey(keys,
244                                         &packets,
245                                         &list_end);
246                                 if (binary) {
247                                         write_openpgp_stream(stdout_putchar,
248                                                         NULL,
249                                                         packets);
250                                 } else {
251                                         armor_openpgp_stream(stdout_putchar,
252                                                 NULL,
253                                                 packets);
254                                 }
255                                 free_packet_list(packets);
256                                 packets = NULL;
257                         }
258                         config.dbbackend->cleanupdb();
259                 } else {
260                         rc = 1;
261                         logthing(LOGTHING_NOTICE, "No keys read.");
262                 }
263
264                 if (keys != NULL) {
265                         free_publickey(keys);
266                         keys = NULL;
267                 } else {
268                         rc = 1;
269                         logthing(LOGTHING_NOTICE, "No changes.");
270                 }
271         } else if (!strcmp("clean", argv[optind])) {
272                 if (binary) {
273                         result = read_openpgp_stream(stdin_getchar, NULL,
274                                  &packets, 0);
275                         logthing(LOGTHING_INFO,
276                                         "read_openpgp_stream: %d", result);
277                 } else {
278                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
279                 }
280
281                 if (packets != NULL) {
282                         result = parse_keys(packets, &keys);
283                         free_packet_list(packets);
284                         packets = NULL;
285                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
286                                         result);
287
288                         if (keys != NULL) {
289                                 result = cleankeys(keys);
290                                 logthing(LOGTHING_INFO, "%d keys cleaned.",
291                                                 result);
292
293                                 flatten_publickey(keys,
294                                         &packets,
295                                         &list_end);
296
297                                 if (binary) {
298                                         write_openpgp_stream(stdout_putchar,
299                                                         NULL,
300                                                         packets);
301                                 } else {
302                                         armor_openpgp_stream(stdout_putchar,
303                                                 NULL,
304                                                 packets);
305                                 }
306                                 free_packet_list(packets);
307                                 packets = NULL;
308                         }
309                 } else {
310                         rc = 1;
311                         logthing(LOGTHING_NOTICE, "No keys read.");
312                 }
313                 
314                 if (keys != NULL) {
315                         free_publickey(keys);
316                         keys = NULL;
317                 }
318         } else if ((argc - optind) == 2) {
319                 search = argv[optind+1];
320                 if (search != NULL && strlen(search) == 42 &&
321                                 search[0] == '0' && search[1] == 'x') {
322                         for (i = 0; i < MAX_FINGERPRINT_LEN; i++) {
323                                 fp[i] = (hex2bin(search[2 + i * 2]) << 4) +
324                                                 hex2bin(search[3 + i * 2]);
325                         }
326                         isfp = true;
327                 } else if (search != NULL) {
328                         keyid = strtoul(search, &end, 16);
329                         if (*search != 0 &&
330                                         end != NULL &&
331                                         *end == 0) {
332                                 ishex = true;
333                         }
334                 }
335                 config.dbbackend->initdb(false);
336                 if (!strcmp("index", argv[optind])) {
337                         find_keys(search, keyid, fp, ishex, isfp,
338                                         fingerprint, skshash,
339                                         false, false);
340                 } else if (!strcmp("vindex", argv[optind])) {
341                         find_keys(search, keyid, fp, ishex, isfp,
342                                         fingerprint, skshash,
343                                         false, true);
344                 } else if (!strcmp("getphoto", argv[optind])) {
345                         if (!ishex) {
346                                 puts("Can't get a key on uid text."
347                                         " You must supply a keyid.");
348                         } else if (config.dbbackend->fetch_key_id(keyid, &keys,
349                                         false)) {
350                                 unsigned char *photo = NULL;
351                                 size_t         length = 0;
352
353                                 if (getphoto(keys, 0, &photo,
354                                                 &length) == ONAK_E_OK) {
355                                         fwrite(photo,
356                                                 1,
357                                                 length,
358                                                 stdout);
359                                 }
360                                 free_publickey(keys);
361                                 keys = NULL;
362                         } else {
363                                 puts("Key not found");
364                         }
365                 } else if (!strcmp("delete", argv[optind])) {
366                         config.dbbackend->delete_key(
367                                         config.dbbackend->getfullkeyid(keyid),
368                                         false);
369                 } else if (!strcmp("get", argv[optind])) {
370                         if (!(ishex || isfp)) {
371                                 puts("Can't get a key on uid text."
372                                         " You must supply a keyid / "
373                                         "fingerprint.");
374                         } else if ((isfp &&
375                                         config.dbbackend->fetch_key_fp(fp,
376                                                 MAX_FINGERPRINT_LEN,
377                                                 &keys, false)) ||
378                                         (ishex &&
379                                         config.dbbackend->fetch_key_id(keyid,
380                                                 &keys, false))) {
381                                 logthing(LOGTHING_INFO, "Got key.");
382                                 flatten_publickey(keys,
383                                                 &packets,
384                                                 &list_end);
385                                 free_publickey(keys);
386                                 if (binary) {
387                                         write_openpgp_stream(stdout_putchar,
388                                                 NULL,
389                                                 packets);
390                                 } else {
391                                         armor_openpgp_stream(stdout_putchar,
392                                                 NULL,
393                                                 packets);
394                                 }
395                                 free_packet_list(packets);
396                                 packets = NULL;
397                         } else {
398                                 puts("Key not found");
399                         }
400                 } else if (!strcmp("hget", argv[optind])) {
401                         if (!parse_skshash(search, &hash)) {
402                                 puts("Couldn't parse sks hash.");
403                         } else if (config.dbbackend->fetch_key_skshash(&hash,
404                                         &keys)) {
405                                 logthing(LOGTHING_INFO, "Got key.");
406                                 flatten_publickey(keys,
407                                                 &packets,
408                                                 &list_end);
409                                 free_publickey(keys);
410                                 if (binary) {
411                                         write_openpgp_stream(stdout_putchar,
412                                                 NULL,
413                                                 packets);
414                                 } else {
415                                         armor_openpgp_stream(stdout_putchar,
416                                                 NULL,
417                                                 packets);
418                                 }
419                                 free_packet_list(packets);
420                                 packets = NULL;
421                         } else {
422                                 puts("Key not found");
423                         }
424                 }
425                 config.dbbackend->cleanupdb();
426         } else {
427                 usage();
428         }
429
430         cleanuplogthing();
431         cleanupconfig();
432
433         return rc;
434 }