Update Debian Vcs-* fields to point to git repository
[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, bool ishex,
48                 bool fingerprint, bool skshash, bool exact, bool verbose)
49 {
50         struct openpgp_publickey *publickey = NULL;
51         int count = 0;
52
53         if (ishex) {
54                 count = config.dbbackend->fetch_key(keyid, &publickey, false);
55         } else {
56                 count = config.dbbackend->fetch_key_text(search, &publickey);
57         }
58         if (publickey != NULL) {
59                 key_index(publickey, verbose, fingerprint, skshash, false);
60                 free_publickey(publickey);
61         } else if (count == 0) {
62                 puts("Key not found.");
63         } else {
64                 printf("Found %d keys, but maximum number to return is %d.\n",
65                                 count,
66                                 config.maxkeys);
67                 puts("Try again with a more specific search.");
68         }
69 }
70
71 /**
72  * @brief Context for the keyserver dumping function
73  */
74 struct dump_ctx {
75         /** Keys we've dumped so far to this file */
76         int count;
77         /** Maximum keys to dump per file */
78         int maxcount;
79         /** File descriptor for the current dump file */
80         int fd;
81         /** Number of the current dump file */
82         int filenum;
83         /** Base filename to use for dump files */
84         char *filebase;
85 };
86
87 void dump_func(void *ctx, struct openpgp_publickey *key)
88 {
89         struct openpgp_packet_list *packets = NULL;
90         struct openpgp_packet_list *list_end = NULL;
91         struct dump_ctx *state;
92         char filename[1024];
93
94         state = (struct dump_ctx *) ctx;
95
96         if (state->fd == -1 || state->count++ > state->maxcount) {
97                 if (state->fd != -1) {
98                         close(state->fd);
99                         state->fd = -1;
100                 }
101                 snprintf(filename, 1023, state->filebase, state->filenum);
102                 state->fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
103                 state->filenum++;
104                 state->count = 0;
105         }
106         flatten_publickey(key, &packets, &list_end);
107         write_openpgp_stream(file_putchar, &state->fd, packets);
108         free_packet_list(packets);
109         packets = list_end = NULL;
110
111         return;
112 }
113
114 void usage(void) {
115         puts("onak " ONAK_VERSION " - an OpenPGP keyserver.\n");
116         puts("Usage:\n");
117         puts("\tonak [options] <command> <parameters>\n");
118         puts("\tCommands:\n");
119         puts("\tadd      - read armored OpenPGP keys from stdin and add to the"
120                 " keyserver");
121         puts("\tclean    - read armored OpenPGP keys from stdin, run the"
122                 " cleaning\n\t             routines against them and dump to"
123                 " stdout");
124         puts("\tdelete   - delete a given key from the keyserver");
125         puts("\tdump     - dump all the keys from the keyserver to a file or"
126                 " files\n\t           starting keydump*");
127         puts("\tget      - retrieves the key requested from the keyserver");
128         puts("\tgetphoto - retrieves the first photoid on the given key and"
129                 " dumps to\n\t           stdout");
130         puts("\tindex    - search for a key and list it");
131         puts("\tvindex   - search for a key and list it and its signatures");
132 }
133
134 int main(int argc, char *argv[])
135 {
136         struct openpgp_packet_list      *packets = NULL;
137         struct openpgp_packet_list      *list_end = NULL;
138         struct openpgp_publickey        *keys = NULL;
139         char                            *configfile = NULL;
140         int                              rc = EXIT_SUCCESS;
141         int                              result = 0;
142         char                            *search = NULL;
143         char                            *end = NULL;
144         uint64_t                         keyid = 0;
145         bool                             ishex = false;
146         bool                             verbose = false;
147         bool                             update = false;
148         bool                             binary = false;
149         bool                             fingerprint = false;
150         bool                             skshash = false;
151         int                              optchar;
152         struct dump_ctx                  dumpstate;
153         struct skshash                   hash;
154
155         while ((optchar = getopt(argc, argv, "bc:fsuv")) != -1 ) {
156                 switch (optchar) {
157                 case 'b': 
158                         binary = true;
159                         break;
160                 case 'c':
161                         configfile = strdup(optarg);
162                         break;
163                 case 'f': 
164                         fingerprint = true;
165                         break;
166                 case 's': 
167                         skshash = true;
168                         break;
169                 case 'u': 
170                         update = true;
171                         break;
172                 case 'v': 
173                         verbose = true;
174                         setlogthreshold(LOGTHING_INFO);
175                         break;
176                 }
177         }
178
179         readconfig(configfile);
180         initlogthing("onak", config.logfile);
181         catchsignals();
182
183         if ((argc - optind) < 1) {
184                 usage();
185         } else if (!strcmp("dump", argv[optind])) {
186                 config.dbbackend->initdb(true);
187                 dumpstate.count = dumpstate.filenum = 0;
188                 dumpstate.maxcount = 100000;
189                 dumpstate.fd = -1;
190                 dumpstate.filebase = "keydump.%d.pgp";
191                 config.dbbackend->iterate_keys(dump_func, &dumpstate);
192                 if (dumpstate.fd != -1) {
193                         close(dumpstate.fd);
194                         dumpstate.fd = -1;
195                 }
196                 config.dbbackend->cleanupdb();
197         } else if (!strcmp("add", argv[optind])) {
198                 if (binary) {
199                         result = read_openpgp_stream(stdin_getchar, NULL,
200                                  &packets, 0);
201                         logthing(LOGTHING_INFO,
202                                         "read_openpgp_stream: %d", result);
203                 } else {
204                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
205                 }
206                 if (packets != NULL) {
207                         result = parse_keys(packets, &keys);
208                         free_packet_list(packets);
209                         packets = NULL;
210                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
211                                         result);
212
213                         result = cleankeys(keys);
214                         logthing(LOGTHING_INFO, "%d keys cleaned.",
215                                         result);
216
217                         config.dbbackend->initdb(false);
218                         logthing(LOGTHING_NOTICE, "Got %d new keys.",
219                                         config.dbbackend->update_keys(&keys,
220                                         false));
221                         if (keys != NULL && update) {
222                                 flatten_publickey(keys,
223                                         &packets,
224                                         &list_end);
225                                 if (binary) {
226                                         write_openpgp_stream(stdout_putchar,
227                                                         NULL,
228                                                         packets);
229                                 } else {
230                                         armor_openpgp_stream(stdout_putchar,
231                                                 NULL,
232                                                 packets);
233                                 }
234                                 free_packet_list(packets);
235                                 packets = NULL;
236                         }
237                         config.dbbackend->cleanupdb();
238                 } else {
239                         rc = 1;
240                         logthing(LOGTHING_NOTICE, "No keys read.");
241                 }
242
243                 if (keys != NULL) {
244                         free_publickey(keys);
245                         keys = NULL;
246                 } else {
247                         rc = 1;
248                         logthing(LOGTHING_NOTICE, "No changes.");
249                 }
250         } else if (!strcmp("clean", argv[optind])) {
251                 if (binary) {
252                         result = read_openpgp_stream(stdin_getchar, NULL,
253                                  &packets, 0);
254                         logthing(LOGTHING_INFO,
255                                         "read_openpgp_stream: %d", result);
256                 } else {
257                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
258                 }
259
260                 if (packets != NULL) {
261                         result = parse_keys(packets, &keys);
262                         free_packet_list(packets);
263                         packets = NULL;
264                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
265                                         result);
266
267                         if (keys != NULL) {
268                                 result = cleankeys(keys);
269                                 logthing(LOGTHING_INFO, "%d keys cleaned.",
270                                                 result);
271
272                                 flatten_publickey(keys,
273                                         &packets,
274                                         &list_end);
275
276                                 if (binary) {
277                                         write_openpgp_stream(stdout_putchar,
278                                                         NULL,
279                                                         packets);
280                                 } else {
281                                         armor_openpgp_stream(stdout_putchar,
282                                                 NULL,
283                                                 packets);
284                                 }
285                                 free_packet_list(packets);
286                                 packets = NULL;
287                         }
288                 } else {
289                         rc = 1;
290                         logthing(LOGTHING_NOTICE, "No keys read.");
291                 }
292                 
293                 if (keys != NULL) {
294                         free_publickey(keys);
295                         keys = NULL;
296                 }
297         } else if ((argc - optind) == 2) {
298                 search = argv[optind+1];
299                 if (search != NULL && strlen(search) == 42 &&
300                                 search[0] == '0' && search[1] == 'x') {
301                         /*
302                          * Fingerprint. Truncate to last 64 bits for
303                          * now.
304                          */
305                         keyid = strtoull(&search[26], &end, 16);
306                         if (end != NULL && *end == 0) {
307                                 ishex = true;
308                         }
309                 } else if (search != NULL) {
310                         keyid = strtoul(search, &end, 16);
311                         if (*search != 0 &&
312                                         end != NULL &&
313                                         *end == 0) {
314                                 ishex = true;
315                         }
316                 }
317                 config.dbbackend->initdb(false);
318                 if (!strcmp("index", argv[optind])) {
319                         find_keys(search, keyid, ishex, fingerprint, skshash,
320                                         false, false);
321                 } else if (!strcmp("vindex", argv[optind])) {
322                         find_keys(search, keyid, ishex, fingerprint, skshash,
323                                         false, true);
324                 } else if (!strcmp("getphoto", argv[optind])) {
325                         if (!ishex) {
326                                 puts("Can't get a key on uid text."
327                                         " You must supply a keyid.");
328                         } else if (config.dbbackend->fetch_key(keyid, &keys,
329                                         false)) {
330                                 unsigned char *photo = NULL;
331                                 size_t         length = 0;
332
333                                 if (getphoto(keys, 0, &photo,
334                                                 &length) == ONAK_E_OK) {
335                                         fwrite(photo,
336                                                 1,
337                                                 length,
338                                                 stdout);
339                                 }
340                                 free_publickey(keys);
341                                 keys = NULL;
342                         } else {
343                                 puts("Key not found");
344                         }
345                 } else if (!strcmp("delete", argv[optind])) {
346                         config.dbbackend->delete_key(
347                                         config.dbbackend->getfullkeyid(keyid),
348                                         false);
349                 } else if (!strcmp("get", argv[optind])) {
350                         if (!ishex) {
351                                 puts("Can't get a key on uid text."
352                                         " You must supply a keyid.");
353                         } else if (config.dbbackend->fetch_key(keyid, &keys,
354                                         false)) {
355                                 logthing(LOGTHING_INFO, "Got key.");
356                                 flatten_publickey(keys,
357                                                 &packets,
358                                                 &list_end);
359                                 free_publickey(keys);
360                                 if (binary) {
361                                         write_openpgp_stream(stdout_putchar,
362                                                 NULL,
363                                                 packets);
364                                 } else {
365                                         armor_openpgp_stream(stdout_putchar,
366                                                 NULL,
367                                                 packets);
368                                 }
369                                 free_packet_list(packets);
370                                 packets = NULL;
371                         } else {
372                                 puts("Key not found");
373                         }
374                 } else if (!strcmp("hget", argv[optind])) {
375                         if (!parse_skshash(search, &hash)) {
376                                 puts("Couldn't parse sks hash.");
377                         } else if (config.dbbackend->fetch_key_skshash(&hash,
378                                         &keys)) {
379                                 logthing(LOGTHING_INFO, "Got key.");
380                                 flatten_publickey(keys,
381                                                 &packets,
382                                                 &list_end);
383                                 free_publickey(keys);
384                                 if (binary) {
385                                         write_openpgp_stream(stdout_putchar,
386                                                 NULL,
387                                                 packets);
388                                 } else {
389                                         armor_openpgp_stream(stdout_putchar,
390                                                 NULL,
391                                                 packets);
392                                 }
393                                 free_packet_list(packets);
394                                 packets = NULL;
395                         } else {
396                                 puts("Key not found");
397                         }
398                 }
399                 config.dbbackend->cleanupdb();
400         } else {
401                 usage();
402         }
403
404         cleanuplogthing();
405         cleanupconfig();
406
407         return rc;
408 }