]> the.earth.li Git - onak.git/blob - keydb/keydb_keyring.c
0.6.3 release
[onak.git] / keydb / keydb_keyring.c
1 /*
2  * keydb_keyring.c - Routines to fetch keys from a PGP keyring file.
3  *
4  * Copyright 2019 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <sys/uio.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "build-config.h"
33 #include "charfuncs.h"
34 #include "keyarray.h"
35 #include "keydb.h"
36 #include "keyid.h"
37 #include "keystructs.h"
38 #include "log.h"
39 #include "mem.h"
40 #include "onak.h"
41 #include "onak-conf.h"
42 #include "parsekey.h"
43
44 struct onak_keyring_dbctx {
45         uint8_t *file;
46         size_t   length;
47         unsigned int space;
48         unsigned int count;
49         struct {
50                 struct openpgp_fingerprint fp;
51                 uint8_t *start;
52                 size_t len;
53         } *keys;
54 };
55
56 /**
57  *      starttrans - Start a transaction.
58  *
59  *      This is just a no-op for keyring file access.
60  */
61 static bool keyring_starttrans(__unused struct onak_dbctx *dbctx)
62 {
63         return true;
64 }
65
66 /**
67  *      endtrans - End a transaction.
68  *
69  *      This is just a no-op for keyring file access.
70  */
71 static void keyring_endtrans(__unused struct onak_dbctx *dbctx)
72 {
73         return;
74 }
75
76 /**
77  * keyring_fetch_key - fetch a key given its index
78  */
79 static int keyring_fetch_key_idx(struct onak_keyring_dbctx *privctx,
80                 unsigned int index,
81                 struct openpgp_publickey **publickey)
82 {
83         struct openpgp_packet_list *packets = NULL;
84         struct buffer_ctx buf;
85
86         if (index > privctx->count)
87                 return 0;
88
89         buf.buffer = (char *) privctx->keys[index].start;
90         buf.size = privctx->keys[index].len;
91         buf.offset = 0;
92
93         read_openpgp_stream(buffer_fetchchar, &buf, &packets, 0);
94         parse_keys(packets, publickey);
95         free_packet_list(packets);
96         packets = NULL;
97
98         return 1;
99 }
100
101 /*
102  * We only index the primary fingerprint of the key, so will only return one
103  * key at most from this function.
104  */
105 static int keyring_fetch_key(struct onak_dbctx *dbctx,
106                         struct openpgp_fingerprint *fingerprint,
107                         struct openpgp_publickey **publickey,
108                         __unused bool intrans)
109 {
110         struct onak_keyring_dbctx *privctx =
111                 (struct onak_keyring_dbctx *) dbctx->priv;
112         int i;
113
114         for (i = 0; i < privctx->count; i++) {
115                 if (fingerprint_cmp(fingerprint, &privctx->keys[i].fp) == 0)
116                         break;
117         }
118
119         if (i < privctx->count) {
120                 return keyring_fetch_key_idx(privctx, i, publickey);
121         }
122
123         return 0;
124 }
125
126 /**
127  *      fetch_key_id - Given a keyid fetch the key from storage.
128  *      @keyid: The keyid to fetch.
129  *      @publickey: A pointer to a structure to return the key in.
130  *      @intrans: If we're already in a transaction.
131  */
132 static int keyring_fetch_key_id(struct onak_dbctx *dbctx,
133                 uint64_t keyid,
134                 struct openpgp_publickey **publickey,
135                 __unused bool intrans)
136 {
137         struct onak_keyring_dbctx *privctx =
138                 (struct onak_keyring_dbctx *) dbctx->priv;
139         int count, i;
140
141         count = 0;
142         for (i = 0; i < privctx->count; i++) {
143                 if (fingerprint2keyid(&privctx->keys[i].fp) == keyid) {
144                         if (keyring_fetch_key_idx(privctx, i, publickey))
145                                 count++;
146                 }
147         }
148
149         return count;
150 }
151
152 /**
153  *      store_key - Takes a key and stores it.
154  *      @publickey: A pointer to the public key to store.
155  *      @intrans: If we're already in a transaction.
156  *      @update: If true the key exists and should be updated.
157  *
158  *      We don't support storing keys into a keyring file.
159  */
160 static int keyring_store_key(__unused struct onak_dbctx *dbctx,
161                 __unused struct openpgp_publickey *publickey,
162                 __unused bool intrans,
163                 __unused bool update)
164 {
165         return 0;
166 }
167
168 /**
169  *      delete_key - Given a keyid delete the key from storage.
170  *      @fp: The fingerprint of the key to delete.
171  *      @intrans: If we're already in a transaction.
172  *
173  *      We don't support removing keys from a keyring file.
174  */
175 static int keyring_delete_key(__unused struct onak_dbctx *dbctx,
176                 __unused struct openpgp_fingerprint *fp, __unused bool intrans)
177 {
178         return 1;
179 }
180
181 /**
182  *      fetch_key_text - Trys to find the keys that contain the supplied text.
183  *      @search: The text to search for.
184  *      @publickey: A pointer to a structure to return the key in.
185  *
186  *      This function searches for the supplied text and returns the keys that
187  *      contain it.
188  *
189  *      TODO: Write for flat file access. Some sort of grep?
190  */
191 static int keyring_fetch_key_text(__unused struct onak_dbctx *dbctx,
192                 __unused const char *search,
193                 __unused struct openpgp_publickey **publickey)
194 {
195         return 0;
196 }
197
198 /**
199  *      iterate_keys - call a function once for each key in the db.
200  *      @iterfunc: The function to call.
201  *      @ctx: A context pointer
202  *
203  *      Calls iterfunc once for each key in the database. ctx is passed
204  *      unaltered to iterfunc. This function is intended to aid database dumps
205  *      and statistic calculations.
206  *
207  *      Returns the number of keys we iterated over.
208  */
209 static int keyring_iterate_keys(struct onak_dbctx *dbctx,
210                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
211                 void *ctx)
212 {
213         struct onak_keyring_dbctx *privctx =
214                 (struct onak_keyring_dbctx *) dbctx->priv;
215         struct openpgp_publickey  *key = NULL;
216         int count, i;
217
218         count = 0;
219         for (i = 0; i < privctx->count; i++) {
220                 if (keyring_fetch_key_idx(privctx, i, &key)) {
221                         iterfunc(ctx, key);
222                         free_publickey(key);
223                         key = NULL;
224                 }
225         }
226
227         return count;
228 }
229
230 static int keyring_update_keys(__unused struct onak_dbctx *dbctx,
231                 __unused struct openpgp_publickey **keys,
232                 __unused struct keyarray *blacklist,
233                 __unused bool updateonly,
234                 __unused bool sendsync)
235 {
236         return 0;
237 }
238
239 /*
240  * Include the basic keydb routines.
241  */
242 #define NEED_KEYID2UID 1
243 #define NEED_GETKEYSIGS 1
244 #include "keydb.c"
245
246 static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
247 {
248         size_t len, pos, start, totlen;
249         struct openpgp_publickey *key;
250         uint8_t tag;
251
252         if (privctx == NULL) {
253                 return 0;
254         }
255
256         if (privctx->file == NULL) {
257                 return 0;
258         }
259
260         /*
261          * Walk the keyring file, noting the start of each public key and the
262          * total length of packets associated with it.
263          */
264         len = pos = start = totlen = 0;
265         while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
266                 if (privctx->file[pos] & 0x40) {
267                         tag = privctx->file[pos] & 0x3F;
268                         len = privctx->file[pos + 1];
269                         if (len > 191 && len < 224) {
270                                 len -= 192;
271                                 len <<= 8;
272                                 len += privctx->file[pos + 2];
273                                 len += 192;
274                                 len += 1; /* Header */
275                         } else if (len > 223 && len < 255) {
276                                 // Unsupported
277                         } else if (len == 255) {
278                                 len = privctx->file[pos + 2];
279                                 len <<= 8;
280                                 len += privctx->file[pos + 3];
281                                 len <<= 8;
282                                 len += privctx->file[pos + 4];
283                                 len <<= 8;
284                                 len += privctx->file[pos + 5];
285                                 len += 4; /* Header */
286                         }
287                         len += 2; /* Header */
288                 } else {
289                         tag = (privctx->file[pos] & 0x3C) >> 2;
290                         switch (privctx->file[pos] & 3) {
291                         case 0:
292                                 len = privctx->file[pos + 1];
293                                 len += 2; /* Header */
294                                 break;
295                         case 1:
296                                 len = privctx->file[pos + 1];
297                                 len <<= 8;
298                                 len += privctx->file[pos + 2];
299                                 len += 3; /* Header */
300                                 break;
301                         case 2:
302                                 len = privctx->file[pos + 1];
303                                 len <<= 8;
304                                 len += privctx->file[pos + 2];
305                                 len <<= 8;
306                                 len += privctx->file[pos + 3];
307                                 len <<= 8;
308                                 len += privctx->file[pos + 4];
309                                 len += 5; /* Header */
310                                 break;
311                         case 3:
312                                 // Unsupported
313                                 break;
314                         }
315                 }
316                 if (tag == OPENPGP_PACKET_PUBLICKEY) {
317                         if (totlen > 0) {
318                                 /* Expand the array of keys if necessary */
319                                 if (privctx->count == privctx->space) {
320                                         privctx->space *= 2;
321                                         privctx->keys = realloc(privctx->keys,
322                                                 privctx->space *
323                                                 sizeof(*privctx->keys));
324                                 }
325
326                                 /* TODO: Sort by fingerprint? */
327                                 privctx->keys[privctx->count].start =
328                                         &privctx->file[start];
329                                 privctx->keys[privctx->count].len = totlen;
330                                 privctx->count++;
331
332                                 /*
333                                  * We need to fetch the key to calculate the
334                                  * fingerprint.
335                                  */
336                                 keyring_fetch_key_idx(privctx,
337                                                 privctx->count - 1,
338                                                 &key);
339                                 get_fingerprint(key->publickey,
340                                         &privctx->keys[privctx->count - 1].fp);
341                                 free_publickey(key);
342                                 key = NULL;
343                         }
344                         start = pos;
345                         totlen = 0;
346                 }
347                 totlen += len;
348                 pos += len;
349         }
350
351         return privctx->count;
352 }
353
354 /**
355  *      cleanupdb - De-initialize the key database.
356  *
357  *      This is just a no-op for flat file access.
358  */
359 static void keyring_cleanupdb(struct onak_dbctx *dbctx)
360 {
361         struct onak_keyring_dbctx *privctx =
362                 (struct onak_keyring_dbctx *) dbctx->priv;
363
364         if (dbctx->priv != NULL) {
365                 if (privctx->file != NULL) {
366                         munmap(privctx->file, privctx->length);
367                 }
368                 free(privctx->keys);
369                 free(dbctx->priv);
370                 dbctx->priv = NULL;
371         }
372
373         if (dbctx != NULL) {
374                 free(dbctx);
375         }
376 }
377
378 /**
379  *      initdb - Initialize the key database.
380  *
381  *      This is just a no-op for flat file access.
382  */
383 struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
384                 __unused bool readonly)
385 {
386         struct onak_keyring_dbctx *privctx;
387         struct onak_dbctx *dbctx;
388         struct stat sb;
389         int fd;
390
391         dbctx = malloc(sizeof(struct onak_dbctx));
392         if (dbctx == NULL) {
393                 return NULL;
394         }
395         dbctx->config = dbcfg;
396         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
397         if (privctx == NULL) {
398                 free(dbctx);
399                 return NULL;
400         }
401         privctx->space = 16;
402         privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
403
404         fd = open(dbcfg->location, O_RDONLY);
405         if (fd < 0) {
406                 logthing(LOGTHING_CRITICAL,
407                                 "Couldn't open keyring file %s: %s (%d)",
408                                 dbcfg->location,
409                                 strerror(errno),
410                                 errno);
411                 keyring_cleanupdb(dbctx);
412                 return NULL;
413         }
414         if (fstat(fd, &sb) < 0) {
415                 logthing(LOGTHING_CRITICAL,
416                                 "Couldn't stat keyring file %s: %s (%d)",
417                                 dbcfg->location,
418                                 strerror(errno),
419                                 errno);
420                 close(fd);
421                 keyring_cleanupdb(dbctx);
422                 return NULL;
423         }
424         privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
425         if (privctx->file == MAP_FAILED) {
426                 logthing(LOGTHING_CRITICAL,
427                                 "Couldn't mmap keyring file %s: %s (%d)",
428                                 dbcfg->location,
429                                 strerror(errno),
430                                 errno);
431                 privctx->file = NULL;
432                 close(fd);
433                 keyring_cleanupdb(dbctx);
434                 return NULL;
435         }
436         privctx->length = sb.st_size;
437         close(fd);
438
439         if (keyring_parse_keys(privctx) == 0) {
440                 logthing(LOGTHING_CRITICAL,
441                                 "Failed to load any keys from keyring file %s",
442                                 dbcfg->location);
443                 keyring_cleanupdb(dbctx);
444                 return NULL;
445         }
446
447         dbctx->cleanupdb                = keyring_cleanupdb;
448         dbctx->starttrans               = keyring_starttrans;
449         dbctx->endtrans                 = keyring_endtrans;
450         dbctx->fetch_key                = keyring_fetch_key;
451         /* We don't index by subkey fingerprint, so fallback to fetch_key */
452         dbctx->fetch_key_fp             = keyring_fetch_key;
453         dbctx->fetch_key_id             = keyring_fetch_key_id;
454         dbctx->fetch_key_text           = keyring_fetch_key_text;
455         dbctx->store_key                = keyring_store_key;
456         dbctx->update_keys              = keyring_update_keys;
457         dbctx->delete_key               = keyring_delete_key;
458         dbctx->getkeysigs               = generic_getkeysigs;
459         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
460         dbctx->keyid2uid                = generic_keyid2uid;
461         dbctx->iterate_keys             = keyring_iterate_keys;
462
463         return dbctx;
464 }