]> the.earth.li Git - onak.git/blob - keydb_keyring.c
0f24366d05864106527d1721010bb1da68b49db5
[onak.git] / 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 sendsync)
228 {
229         return 0;
230 }
231
232 /*
233  * Include the basic keydb routines.
234  */
235 #define NEED_KEYID2UID 1
236 #define NEED_GETKEYSIGS 1
237 #include "keydb.c"
238
239 static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
240 {
241         size_t len, pos, start, totlen;
242         struct openpgp_publickey *key;
243         uint8_t tag;
244
245         if (privctx == NULL) {
246                 return 0;
247         }
248
249         if (privctx->file == NULL) {
250                 return 0;
251         }
252
253         /*
254          * Walk the keyring file, noting the start of each public key and the
255          * total length of packets associated with it.
256          */
257         len = pos = start = totlen = 0;
258         while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
259                 if (privctx->file[pos] & 0x40) {
260                         tag = privctx->file[pos] & 0x3F;
261                         len = privctx->file[pos + 1];
262                         if (len > 191 && len < 224) {
263                                 len -= 192;
264                                 len <<= 8;
265                                 len += privctx->file[pos + 2];
266                                 len += 192;
267                                 len += 1; /* Header */
268                         } else if (len > 223 && len < 255) {
269                                 // Unsupported
270                         } else if (len == 255) {
271                                 len = privctx->file[pos + 2];
272                                 len <<= 8;
273                                 len += privctx->file[pos + 3];
274                                 len <<= 8;
275                                 len += privctx->file[pos + 4];
276                                 len <<= 8;
277                                 len += privctx->file[pos + 5];
278                                 len += 4; /* Header */
279                         }
280                         len += 2; /* Header */
281                 } else {
282                         tag = (privctx->file[pos] & 0x3C) >> 2;
283                         switch (privctx->file[pos] & 3) {
284                         case 0:
285                                 len = privctx->file[pos + 1];
286                                 len += 2; /* Header */
287                                 break;
288                         case 1:
289                                 len = privctx->file[pos + 1];
290                                 len <<= 8;
291                                 len += privctx->file[pos + 2];
292                                 len += 3; /* Header */
293                                 break;
294                         case 2:
295                                 len = privctx->file[pos + 1];
296                                 len <<= 8;
297                                 len += privctx->file[pos + 2];
298                                 len <<= 8;
299                                 len += privctx->file[pos + 3];
300                                 len <<= 8;
301                                 len += privctx->file[pos + 4];
302                                 len += 5; /* Header */
303                                 break;
304                         case 3:
305                                 // Unsupported
306                                 break;
307                         }
308                 }
309                 if (tag == OPENPGP_PACKET_PUBLICKEY) {
310                         if (totlen > 0) {
311                                 /* Expand the array of keys if necessary */
312                                 if (privctx->count == privctx->space) {
313                                         privctx->space *= 2;
314                                         privctx->keys = realloc(privctx->keys,
315                                                 privctx->space *
316                                                 sizeof(*privctx->keys));
317                                 }
318
319                                 /* TODO: Sort by fingerprint? */
320                                 privctx->keys[privctx->count].start =
321                                         &privctx->file[start];
322                                 privctx->keys[privctx->count].len = totlen;
323                                 privctx->count++;
324
325                                 /*
326                                  * We need to fetch the key to calculate the
327                                  * fingerprint.
328                                  */
329                                 keyring_fetch_key(privctx, privctx->count - 1,
330                                                 &key);
331                                 get_fingerprint(key->publickey,
332                                         &privctx->keys[privctx->count - 1].fp);
333                                 free_publickey(key);
334                                 key = NULL;
335                         }
336                         start = pos;
337                         totlen = 0;
338                 }
339                 totlen += len;
340                 pos += len;
341         }
342
343         return privctx->count;
344 }
345
346 /**
347  *      cleanupdb - De-initialize the key database.
348  *
349  *      This is just a no-op for flat file access.
350  */
351 static void keyring_cleanupdb(struct onak_dbctx *dbctx)
352 {
353         struct onak_keyring_dbctx *privctx =
354                 (struct onak_keyring_dbctx *) dbctx->priv;
355
356         if (dbctx->priv != NULL) {
357                 if (privctx->file != NULL) {
358                         munmap(privctx->file, privctx->length);
359                 }
360                 free(privctx->keys);
361                 free(dbctx->priv);
362                 dbctx->priv = NULL;
363         }
364
365         if (dbctx != NULL) {
366                 free(dbctx);
367         }
368 };
369
370 /**
371  *      initdb - Initialize the key database.
372  *
373  *      This is just a no-op for flat file access.
374  */
375 struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
376                 bool readonly)
377 {
378         struct onak_keyring_dbctx *privctx;
379         struct onak_dbctx *dbctx;
380         struct stat sb;
381         int fd;
382
383         dbctx = malloc(sizeof(struct onak_dbctx));
384         if (dbctx == NULL) {
385                 return NULL;
386         }
387         dbctx->config = dbcfg;
388         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
389         if (privctx == NULL) {
390                 free(dbctx);
391                 return NULL;
392         }
393         privctx->space = 16;
394         privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
395
396         fd = open(dbcfg->location, O_RDONLY);
397         if (fd < 0) {
398                 logthing(LOGTHING_CRITICAL,
399                                 "Couldn't open keyring file %s: %s (%d)",
400                                 dbcfg->location,
401                                 strerror(errno),
402                                 errno);
403                 keyring_cleanupdb(dbctx);
404                 return NULL;
405         }
406         if (fstat(fd, &sb) < 0) {
407                 logthing(LOGTHING_CRITICAL,
408                                 "Couldn't stat keyring file %s: %s (%d)",
409                                 dbcfg->location,
410                                 strerror(errno),
411                                 errno);
412                 close(fd);
413                 keyring_cleanupdb(dbctx);
414                 return NULL;
415         }
416         privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
417         if (privctx->file == MAP_FAILED) {
418                 logthing(LOGTHING_CRITICAL,
419                                 "Couldn't mmap keyring file %s: %s (%d)",
420                                 dbcfg->location,
421                                 strerror(errno),
422                                 errno);
423                 privctx->file = NULL;
424                 close(fd);
425                 keyring_cleanupdb(dbctx);
426                 return NULL;
427         }
428         privctx->length = sb.st_size;
429         close(fd);
430
431         if (keyring_parse_keys(privctx) == 0) {
432                 logthing(LOGTHING_CRITICAL,
433                                 "Failed to load any keys from keyring file %s",
434                                 dbcfg->location);
435                 keyring_cleanupdb(dbctx);
436                 return NULL;
437         }
438
439         dbctx->cleanupdb                = keyring_cleanupdb;
440         dbctx->starttrans               = keyring_starttrans;
441         dbctx->endtrans                 = keyring_endtrans;
442         dbctx->fetch_key_id             = keyring_fetch_key_id;
443         dbctx->fetch_key_fp             = keyring_fetch_key_fp;
444         dbctx->fetch_key_text           = keyring_fetch_key_text;
445         dbctx->store_key                = keyring_store_key;
446         dbctx->update_keys              = keyring_update_keys;
447         dbctx->delete_key               = keyring_delete_key;
448         dbctx->getkeysigs               = generic_getkeysigs;
449         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
450         dbctx->keyid2uid                = generic_keyid2uid;
451         dbctx->iterate_keys             = keyring_iterate_keys;
452
453         return dbctx;
454 }