]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - usr.sbin/pkg/config.c
Implement pubkey support for pkg(7) bootstrap. [EN-15:18]
[FreeBSD/releng/9.3.git] / usr.sbin / pkg / config.c
1 /*-
2  * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/sbuf.h>
33 #include <sys/elf_common.h>
34 #include <sys/endian.h>
35 #include <sys/types.h>
36
37 #include <dirent.h>
38 #include <ucl.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <gelf.h>
44 #include <inttypes.h>
45 #include <paths.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "elf_tables.h"
51 #include "config.h"
52
53 #define roundup2(x, y)  (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
54
55 struct config_value {
56        char *value;
57        STAILQ_ENTRY(config_value) next;
58 };
59
60 struct config_entry {
61         uint8_t type;
62         const char *key;
63         const char *val;
64         char *value;
65         STAILQ_HEAD(, config_value) *list;
66         bool envset;
67         bool main_only;                         /* Only set in pkg.conf. */
68 };
69
70 static struct config_entry c[] = {
71         [PACKAGESITE] = {
72                 PKG_CONFIG_STRING,
73                 "PACKAGESITE",
74                 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
75                 NULL,
76                 NULL,
77                 false,
78                 false,
79         },
80         [ABI] = {
81                 PKG_CONFIG_STRING,
82                 "ABI",
83                 NULL,
84                 NULL,
85                 NULL,
86                 false,
87                 true,
88         },
89         [MIRROR_TYPE] = {
90                 PKG_CONFIG_STRING,
91                 "MIRROR_TYPE",
92                 "SRV",
93                 NULL,
94                 NULL,
95                 false,
96                 false,
97         },
98         [ASSUME_ALWAYS_YES] = {
99                 PKG_CONFIG_BOOL,
100                 "ASSUME_ALWAYS_YES",
101                 "NO",
102                 NULL,
103                 NULL,
104                 false,
105                 true,
106         },
107         [SIGNATURE_TYPE] = {
108                 PKG_CONFIG_STRING,
109                 "SIGNATURE_TYPE",
110                 NULL,
111                 NULL,
112                 NULL,
113                 false,
114                 false,
115         },
116         [FINGERPRINTS] = {
117                 PKG_CONFIG_STRING,
118                 "FINGERPRINTS",
119                 NULL,
120                 NULL,
121                 NULL,
122                 false,
123                 false,
124         },
125         [REPOS_DIR] = {
126                 PKG_CONFIG_LIST,
127                 "REPOS_DIR",
128                 NULL,
129                 NULL,
130                 NULL,
131                 false,
132                 true,
133         },
134         [PUBKEY] = {
135                 PKG_CONFIG_STRING,
136                 "PUBKEY",
137                 NULL,
138                 NULL,
139                 NULL,
140                 false,
141                 false
142         }
143 };
144
145 static const char *
146 elf_corres_to_string(struct _elf_corres *m, int e)
147 {
148         int i;
149
150         for (i = 0; m[i].string != NULL; i++)
151                 if (m[i].elf_nb == e)
152                         return (m[i].string);
153
154         return ("unknown");
155 }
156
157 static int
158 pkg_get_myabi(char *dest, size_t sz)
159 {
160         Elf *elf;
161         Elf_Data *data;
162         Elf_Note note;
163         Elf_Scn *scn;
164         char *src, *osname;
165         const char *abi;
166         GElf_Ehdr elfhdr;
167         GElf_Shdr shdr;
168         int fd, i, ret;
169         uint32_t version;
170
171         version = 0;
172         ret = -1;
173         scn = NULL;
174         abi = NULL;
175
176         if (elf_version(EV_CURRENT) == EV_NONE) {
177                 warnx("ELF library initialization failed: %s",
178                     elf_errmsg(-1));
179                 return (-1);
180         }
181
182         if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
183                 warn("open()");
184                 return (-1);
185         }
186
187         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
188                 ret = -1;
189                 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
190                 goto cleanup;
191         }
192
193         if (gelf_getehdr(elf, &elfhdr) == NULL) {
194                 ret = -1;
195                 warn("getehdr() failed: %s.", elf_errmsg(-1));
196                 goto cleanup;
197         }
198         while ((scn = elf_nextscn(elf, scn)) != NULL) {
199                 if (gelf_getshdr(scn, &shdr) != &shdr) {
200                         ret = -1;
201                         warn("getshdr() failed: %s.", elf_errmsg(-1));
202                         goto cleanup;
203                 }
204
205                 if (shdr.sh_type == SHT_NOTE)
206                         break;
207         }
208
209         if (scn == NULL) {
210                 ret = -1;
211                 warn("failed to get the note section");
212                 goto cleanup;
213         }
214
215         data = elf_getdata(scn, NULL);
216         src = data->d_buf;
217         for (;;) {
218                 memcpy(&note, src, sizeof(Elf_Note));
219                 src += sizeof(Elf_Note);
220                 if (note.n_type == NT_VERSION)
221                         break;
222                 src += note.n_namesz + note.n_descsz;
223         }
224         osname = src;
225         src += roundup2(note.n_namesz, 4);
226         if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
227                 version = be32dec(src);
228         else
229                 version = le32dec(src);
230
231         for (i = 0; osname[i] != '\0'; i++)
232                 osname[i] = (char)tolower(osname[i]);
233
234         snprintf(dest, sz, "%s:%d:%s:%s",
235             osname, version / 100000,
236             elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
237             elf_corres_to_string(wordsize_corres,
238             (int)elfhdr.e_ident[EI_CLASS]));
239
240         ret = 0;
241
242         switch (elfhdr.e_machine) {
243         case EM_ARM:
244                 snprintf(dest + strlen(dest), sz - strlen(dest),
245                     ":%s:%s:%s", elf_corres_to_string(endian_corres,
246                     (int)elfhdr.e_ident[EI_DATA]),
247                     (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
248                     "eabi" : "oabi",
249                     (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
250                     "softfp" : "vfp");
251                 break;
252         case EM_MIPS:
253                 /*
254                  * this is taken from binutils sources:
255                  * include/elf/mips.h
256                  * mapping is figured out from binutils:
257                  * gas/config/tc-mips.c
258                  */
259                 switch (elfhdr.e_flags & EF_MIPS_ABI) {
260                 case E_MIPS_ABI_O32:
261                         abi = "o32";
262                         break;
263                 case E_MIPS_ABI_N32:
264                         abi = "n32";
265                         break;
266                 default:
267                         if (elfhdr.e_ident[EI_DATA] ==
268                             ELFCLASS32)
269                                 abi = "o32";
270                         else if (elfhdr.e_ident[EI_DATA] ==
271                             ELFCLASS64)
272                                 abi = "n64";
273                         break;
274                 }
275                 snprintf(dest + strlen(dest), sz - strlen(dest),
276                     ":%s:%s", elf_corres_to_string(endian_corres,
277                     (int)elfhdr.e_ident[EI_DATA]), abi);
278                 break;
279         }
280
281 cleanup:
282         if (elf != NULL)
283                 elf_end(elf);
284
285         close(fd);
286         return (ret);
287 }
288
289 static void
290 subst_packagesite(const char *abi)
291 {
292         struct sbuf *newval;
293         const char *variable_string;
294         const char *oldval;
295
296         if (c[PACKAGESITE].value != NULL)
297                 oldval = c[PACKAGESITE].value;
298         else
299                 oldval = c[PACKAGESITE].val;
300
301         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
302                 return;
303
304         newval = sbuf_new_auto();
305         sbuf_bcat(newval, oldval, variable_string - oldval);
306         sbuf_cat(newval, abi);
307         sbuf_cat(newval, variable_string + strlen("${ABI}"));
308         sbuf_finish(newval);
309
310         free(c[PACKAGESITE].value);
311         c[PACKAGESITE].value = strdup(sbuf_data(newval));
312 }
313
314 static int
315 boolstr_to_bool(const char *str)
316 {
317         if (str != NULL && (strcasecmp(str, "true") == 0 ||
318             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
319             str[0] == '1'))
320                 return (true);
321
322         return (false);
323 }
324
325 static void
326 config_parse(ucl_object_t *obj, pkg_conf_file_t conftype)
327 {
328         struct sbuf *buf = sbuf_new_auto();
329         ucl_object_t *cur, *seq;
330         ucl_object_iter_t it = NULL, itseq = NULL;
331         struct config_entry *temp_config;
332         struct config_value *cv;
333         const char *key;
334         int i;
335         size_t j;
336
337         /* Temporary config for configs that may be disabled. */
338         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
339
340         while ((cur = ucl_iterate_object(obj, &it, true))) {
341                 key = ucl_object_key(cur);
342                 if (key == NULL)
343                         continue;
344                 sbuf_clear(buf);
345
346                 if (conftype == CONFFILE_PKG) {
347                         for (j = 0; j < strlen(key); ++j)
348                                 sbuf_putc(buf, key[j]);
349                         sbuf_finish(buf);
350                 } else if (conftype == CONFFILE_REPO) {
351                         if (strcasecmp(key, "url") == 0)
352                                 sbuf_cpy(buf, "PACKAGESITE");
353                         else if (strcasecmp(key, "mirror_type") == 0)
354                                 sbuf_cpy(buf, "MIRROR_TYPE");
355                         else if (strcasecmp(key, "signature_type") == 0)
356                                 sbuf_cpy(buf, "SIGNATURE_TYPE");
357                         else if (strcasecmp(key, "fingerprints") == 0)
358                                 sbuf_cpy(buf, "FINGERPRINTS");
359                         else if (strcasecmp(key, "pubkey") == 0)
360                                 sbuf_cpy(buf, "PUBKEY");
361                         else if (strcasecmp(key, "enabled") == 0) {
362                                 if ((cur->type != UCL_BOOLEAN) ||
363                                     !ucl_object_toboolean(cur))
364                                         goto cleanup;
365                         } else
366                                 continue;
367                         sbuf_finish(buf);
368                 }
369
370                 for (i = 0; i < CONFIG_SIZE; i++) {
371                         if (strcmp(sbuf_data(buf), c[i].key) == 0)
372                                 break;
373                 }
374
375                 /* Silently skip unknown keys to be future compatible. */
376                 if (i == CONFIG_SIZE)
377                         continue;
378
379                 /* env has priority over config file */
380                 if (c[i].envset)
381                         continue;
382
383                 /* Parse sequence value ["item1", "item2"] */
384                 switch (c[i].type) {
385                 case PKG_CONFIG_LIST:
386                         if (cur->type != UCL_ARRAY) {
387                                 warnx("Skipping invalid array "
388                                     "value for %s.\n", c[i].key);
389                                 continue;
390                         }
391                         temp_config[i].list =
392                             malloc(sizeof(*temp_config[i].list));
393                         STAILQ_INIT(temp_config[i].list);
394
395                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
396                                 if (seq->type != UCL_STRING)
397                                         continue;
398                                 cv = malloc(sizeof(struct config_value));
399                                 cv->value =
400                                     strdup(ucl_object_tostring(seq));
401                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
402                                     next);
403                         }
404                         break;
405                 case PKG_CONFIG_BOOL:
406                         temp_config[i].value =
407                             strdup(ucl_object_toboolean(cur) ? "yes" : "no");
408                         break;
409                 default:
410                         /* Normal string value. */
411                         temp_config[i].value = strdup(ucl_object_tostring(cur));
412                         break;
413                 }
414         }
415
416         /* Repo is enabled, copy over all settings from temp_config. */
417         for (i = 0; i < CONFIG_SIZE; i++) {
418                 if (c[i].envset)
419                         continue;
420                 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
421                 if (conftype != CONFFILE_PKG && c[i].main_only == true)
422                         continue;
423                 switch (c[i].type) {
424                 case PKG_CONFIG_LIST:
425                         c[i].list = temp_config[i].list;
426                         break;
427                 default:
428                         c[i].value = temp_config[i].value;
429                         break;
430                 }
431         }
432
433 cleanup:
434         free(temp_config);
435         sbuf_delete(buf);
436 }
437
438 /*-
439  * Parse new repo style configs in style:
440  * Name:
441  *   URL:
442  *   MIRROR_TYPE:
443  * etc...
444  */
445 static void
446 parse_repo_file(ucl_object_t *obj)
447 {
448         ucl_object_iter_t it = NULL;
449         ucl_object_t *cur;
450         const char *key;
451
452         while ((cur = ucl_iterate_object(obj, &it, true))) {
453                 key = ucl_object_key(cur);
454
455                 if (key == NULL)
456                         continue;
457
458                 if (cur->type != UCL_OBJECT)
459                         continue;
460
461                 config_parse(cur, CONFFILE_REPO);
462         }
463 }
464
465
466 static int
467 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
468 {
469         struct ucl_parser *p;
470         ucl_object_t *obj = NULL;
471
472         p = ucl_parser_new(0);
473
474         if (!ucl_parser_add_file(p, confpath)) {
475                 if (errno != ENOENT)
476                         errx(EXIT_FAILURE, "Unable to parse configuration "
477                             "file %s: %s", confpath, ucl_parser_get_error(p));
478                 ucl_parser_free(p);
479                 /* no configuration present */
480                 return (1);
481         }
482
483         obj = ucl_parser_get_object(p);
484         if (obj->type != UCL_OBJECT) 
485                 warnx("Invalid configuration format, ignoring the "
486                     "configuration file %s", confpath);
487         else {
488                 if (conftype == CONFFILE_PKG)
489                         config_parse(obj, conftype);
490                 else if (conftype == CONFFILE_REPO)
491                         parse_repo_file(obj);
492         }
493
494         ucl_object_free(obj);
495         ucl_parser_free(p);
496
497         return (0);
498 }
499
500 static int
501 load_repositories(const char *repodir)
502 {
503         struct dirent *ent;
504         DIR *d;
505         char *p;
506         size_t n;
507         char path[MAXPATHLEN];
508         int ret;
509
510         ret = 0;
511
512         if ((d = opendir(repodir)) == NULL)
513                 return (1);
514
515         while ((ent = readdir(d))) {
516                 /* Trim out 'repos'. */
517                 if ((n = strlen(ent->d_name)) <= 5)
518                         continue;
519                 p = &ent->d_name[n - 5];
520                 if (strcmp(p, ".conf") == 0) {
521                         snprintf(path, sizeof(path), "%s%s%s",
522                             repodir,
523                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
524                             ent->d_name);
525                         if (access(path, F_OK) == 0 &&
526                             read_conf_file(path, CONFFILE_REPO)) {
527                                 ret = 1;
528                                 goto cleanup;
529                         }
530                 }
531         }
532
533 cleanup:
534         closedir(d);
535
536         return (ret);
537 }
538
539 int
540 config_init(void)
541 {
542         char *val;
543         int i;
544         const char *localbase;
545         char *env_list_item;
546         char confpath[MAXPATHLEN];
547         struct config_value *cv;
548         char abi[BUFSIZ];
549
550         for (i = 0; i < CONFIG_SIZE; i++) {
551                 val = getenv(c[i].key);
552                 if (val != NULL) {
553                         c[i].envset = true;
554                         switch (c[i].type) {
555                         case PKG_CONFIG_LIST:
556                                 /* Split up comma-separated items from env. */
557                                 c[i].list = malloc(sizeof(*c[i].list));
558                                 STAILQ_INIT(c[i].list);
559                                 for (env_list_item = strtok(val, ",");
560                                     env_list_item != NULL;
561                                     env_list_item = strtok(NULL, ",")) {
562                                         cv =
563                                             malloc(sizeof(struct config_value));
564                                         cv->value =
565                                             strdup(env_list_item);
566                                         STAILQ_INSERT_TAIL(c[i].list, cv,
567                                             next);
568                                 }
569                                 break;
570                         default:
571                                 c[i].val = val;
572                                 break;
573                         }
574                 }
575         }
576
577         /* Read LOCALBASE/etc/pkg.conf first. */
578         localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
579         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
580             localbase);
581
582         if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
583             CONFFILE_PKG))
584                 goto finalize;
585
586         /* Then read in all repos from REPOS_DIR list of directories. */
587         if (c[REPOS_DIR].list == NULL) {
588                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
589                 STAILQ_INIT(c[REPOS_DIR].list);
590                 cv = malloc(sizeof(struct config_value));
591                 cv->value = strdup("/etc/pkg");
592                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
593                 cv = malloc(sizeof(struct config_value));
594                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
595                         goto finalize;
596                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
597         }
598
599         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
600                 if (load_repositories(cv->value))
601                         goto finalize;
602
603 finalize:
604         if (c[ABI].val == NULL && c[ABI].value == NULL) {
605                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
606                         errx(EXIT_FAILURE, "Failed to determine the system "
607                             "ABI");
608                 c[ABI].val = abi;
609         }
610
611         subst_packagesite(c[ABI].val);
612
613         return (0);
614 }
615
616 int
617 config_string(pkg_config_key k, const char **val)
618 {
619         if (c[k].type != PKG_CONFIG_STRING)
620                 return (-1);
621
622         if (c[k].value != NULL)
623                 *val = c[k].value;
624         else
625                 *val = c[k].val;
626
627         return (0);
628 }
629
630 int
631 config_bool(pkg_config_key k, bool *val)
632 {
633         const char *value;
634
635         if (c[k].type != PKG_CONFIG_BOOL)
636                 return (-1);
637
638         *val = false;
639
640         if (c[k].value != NULL)
641                 value = c[k].value;
642         else
643                 value = c[k].val;
644
645         if (boolstr_to_bool(value))
646                 *val = true;
647
648         return (0);
649 }
650
651 void
652 config_finish(void) {
653         int i;
654
655         for (i = 0; i < CONFIG_SIZE; i++)
656                 free(c[i].value);
657 }