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