static int analyse_elf(struct pkg *pkg, const char *fpath, int (action)(void *, struct pkg *, const char *, const char *, bool), void *actdata) { Elf *e = NULL; GElf_Ehdr elfhdr; Elf_Scn *scn = NULL; Elf_Scn *note = NULL; Elf_Scn *dynamic = NULL; GElf_Shdr shdr; Elf_Data *data; GElf_Dyn *dyn, dyn_mem; struct stat sb; int ret = EPKG_OK; size_t numdyn = 0; size_t sh_link = 0; size_t dynidx; const char *osname; const char *shlib; bool developer = false; bool is_shlib = false; pkg_config_bool(PKG_CONFIG_DEVELOPER_MODE, &developer); int fd; if (lstat(fpath, &sb) != 0) pkg_emit_errno("fstat() failed for", fpath); /* ignore empty files and non regular files */ if (sb.st_size == 0 || !S_ISREG(sb.st_mode)) return (EPKG_END); /* Empty file or sym-link: no results */ if ((fd = open(fpath, O_RDONLY, 0)) < 0) { return (EPKG_FATAL); } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("elf_begin() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (elf_kind(e) != ELF_K_ELF) { /* Not an elf file: no results */ ret = EPKG_END; goto cleanup; } if (developer) pkg->flags |= PKG_CONTAINS_ELF_OBJECTS; if (gelf_getehdr(e, &elfhdr) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getehdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { ret = EPKG_FATAL; pkg_emit_error("getshdr() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } switch (shdr.sh_type) { case SHT_NOTE: note = scn; break; case SHT_DYNAMIC: dynamic = scn; sh_link = shdr.sh_link; numdyn = shdr.sh_size / shdr.sh_entsize; break; } if (note != NULL && dynamic != NULL) break; } /* * note == NULL usually means a shared object for use with dlopen(3) * dynamic == NULL means not a dynamically linked elf */ if (dynamic == NULL) { ret = EPKG_END; goto cleanup; /* not a dynamically linked elf: no results */ } if (note != NULL) { if ((data = elf_getdata(note, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } if (data->d_buf == NULL) { ret = EPKG_END; /* No osname available */ goto cleanup; } osname = (const char *) data->d_buf + sizeof(Elf_Note); if (strncasecmp(osname, "freebsd", sizeof("freebsd")) != 0 && strncasecmp(osname, "dragonfly", sizeof("dragonfly")) != 0) { ret = EPKG_END; /* Foreign (probably linux) ELF object */ goto cleanup; } } else { if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD) { ret = EPKG_END; goto cleanup; } } if ((data = elf_getdata(dynamic, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } /* First, scan through the data from the .dynamic section to find any RPATH or RUNPATH settings. These are colon separated paths to prepend to the ld.so search paths from the ELF hints file. These always seem to come right after the NEEDED shared library entries. NEEDED entries should resolve to a filename for installed executables, but need not resolve for installed shared libraries -- additional info from the apps that link against them would be required. Shared libraries are distinguished by a DT_SONAME tag */ rpath_list_init(); for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag == DT_SONAME) { is_shlib = true; /* The file being scanned is a shared library *provided* by the package. Record this if appropriate */ pkg_addshlib_provided(pkg, basename(fpath)); } if (dyn->d_tag != DT_RPATH && dyn->d_tag != DT_RUNPATH) continue; shlib_list_from_rpath(elf_strptr(e, sh_link, dyn->d_un.d_val), dirname(fpath)); break; } /* Now find all of the NEEDED shared libraries. */ for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag != DT_NEEDED) continue; shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); action(actdata, pkg, fpath, shlib, is_shlib); } cleanup: rpath_list_free(); if (e != NULL) elf_end(e); close(fd); return (ret); }
static int parse_sequence(struct pkg * pkg, yaml_node_t *node, yaml_document_t *doc, int attr) { yaml_node_item_t *item; yaml_node_t *val; item = node->data.sequence.items.start; while (item < node->data.sequence.items.top) { val = yaml_document_get_node(doc, *item); switch (attr) { case PKG_CATEGORIES: if (!is_valid_yaml_scalar(val)) pkg_emit_error("Skipping malformed category"); else pkg_addcategory(pkg, val->data.scalar.value); break; case PKG_LICENSES: if (!is_valid_yaml_scalar(val)) pkg_emit_error("Skipping malformed license"); else pkg_addlicense(pkg, val->data.scalar.value); break; case PKG_USERS: if (is_valid_yaml_scalar(val)) pkg_adduser(pkg, val->data.scalar.value); else if (val->type == YAML_MAPPING_NODE) parse_mapping(pkg, val, doc, attr); else pkg_emit_error("Skipping malformed license"); break; case PKG_GROUPS: if (is_valid_yaml_scalar(val)) pkg_addgroup(pkg, val->data.scalar.value); else if (val->type == YAML_MAPPING_NODE) parse_mapping(pkg, val, doc, attr); else pkg_emit_error("Skipping malformed license"); break; case PKG_DIRS: if (is_valid_yaml_scalar(val)) pkg_adddir(pkg, val->data.scalar.value, 1, false); else if (val->type == YAML_MAPPING_NODE) parse_mapping(pkg, val, doc, attr); else pkg_emit_error("Skipping malformed dirs"); break; case PKG_SHLIBS_REQUIRED: if (!is_valid_yaml_scalar(val)) pkg_emit_error("Skipping malformed required shared library"); else pkg_addshlib_required(pkg, val->data.scalar.value); break; case PKG_SHLIBS_PROVIDED: if (!is_valid_yaml_scalar(val)) pkg_emit_error("Skipping malformed provided shared library"); else pkg_addshlib_provided(pkg, val->data.scalar.value); break; } ++item; } return (EPKG_OK); }
static int pkg_array(struct pkg *pkg, ucl_object_t *obj, int attr) { ucl_object_t *cur; ucl_object_iter_t it = NULL; pkg_debug(3, "%s", "Manifest: parsing array"); while ((cur = ucl_iterate_object(obj, &it, true))) { switch (attr) { case PKG_CATEGORIES: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed category"); else pkg_addcategory(pkg, ucl_object_tostring(cur)); break; case PKG_LICENSES: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed license"); else pkg_addlicense(pkg, ucl_object_tostring(cur)); break; case PKG_USERS: if (cur->type == UCL_STRING) pkg_adduser(pkg, ucl_object_tostring(cur)); else if (cur->type == UCL_OBJECT) pkg_object(pkg, cur, attr); else pkg_emit_error("Skipping malformed license"); break; case PKG_GROUPS: if (cur->type == UCL_STRING) pkg_addgroup(pkg, ucl_object_tostring(cur)); else if (cur->type == UCL_OBJECT) pkg_object(pkg, cur, attr); else pkg_emit_error("Skipping malformed license"); break; case PKG_DIRS: if (cur->type == UCL_STRING) pkg_adddir(pkg, ucl_object_tostring(cur), 1, false); else if (cur->type == UCL_OBJECT) pkg_object(pkg, cur, attr); else pkg_emit_error("Skipping malformed dirs"); break; case PKG_SHLIBS_REQUIRED: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed required shared library"); else pkg_addshlib_required(pkg, ucl_object_tostring(cur)); break; case PKG_SHLIBS_PROVIDED: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed provided shared library"); else pkg_addshlib_provided(pkg, ucl_object_tostring(cur)); break; case PKG_CONFLICTS: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed conflict name"); else pkg_addconflict(pkg, ucl_object_tostring(cur)); break; case PKG_PROVIDES: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed provide name"); else pkg_addprovide(pkg, ucl_object_tostring(cur)); break; } } return (EPKG_OK); }
static int analyse_elf(struct pkg *pkg, const char *fpath) { Elf *e = NULL; GElf_Ehdr elfhdr; Elf_Scn *scn = NULL; Elf_Scn *note = NULL; Elf_Scn *dynamic = NULL; GElf_Shdr shdr; Elf_Data *data; GElf_Dyn *dyn, dyn_mem; struct stat sb; int ret = EPKG_OK; size_t numdyn = 0; size_t sh_link = 0; size_t dynidx; const char *myarch; const char *shlib; char *rpath = NULL; bool is_shlib = false; myarch = pkg_object_string(pkg_config_get("ABI")); int fd; pkg_debug(1, "analysing elf %s", fpath); if (lstat(fpath, &sb) != 0) pkg_emit_errno("fstat() failed for", fpath); /* ignore empty files and non regular files */ if (sb.st_size == 0 || !S_ISREG(sb.st_mode)) return (EPKG_END); /* Empty file or sym-link: no results */ if ((fd = open(fpath, O_RDONLY, 0)) < 0) { return (EPKG_FATAL); } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("elf_begin() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (elf_kind(e) != ELF_K_ELF) { /* Not an elf file: no results */ ret = EPKG_END; pkg_debug(1, "not an elf"); goto cleanup; } if (ctx.developer_mode) pkg->flags |= PKG_CONTAINS_ELF_OBJECTS; if (gelf_getehdr(e, &elfhdr) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getehdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } if (elfhdr.e_type != ET_DYN && elfhdr.e_type != ET_EXEC && elfhdr.e_type != ET_REL) { pkg_debug(1, "not an elf"); ret = EPKG_END; goto cleanup; } /* Elf file has sections header */ while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { ret = EPKG_FATAL; pkg_emit_error("getshdr() for %s failed: %s", fpath, elf_errmsg(-1)); goto cleanup; } switch (shdr.sh_type) { case SHT_NOTE: if ((data = elf_getdata(scn, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } else if (data->d_buf != NULL) { Elf_Note *en = (Elf_Note *)data->d_buf; if (en->n_type == NT_ABI_TAG) note = scn; } break; case SHT_DYNAMIC: dynamic = scn; sh_link = shdr.sh_link; if (shdr.sh_entsize == 0) { ret = EPKG_END; goto cleanup; } numdyn = shdr.sh_size / shdr.sh_entsize; break; } if (note != NULL && dynamic != NULL) break; } /* * note == NULL usually means a shared object for use with dlopen(3) * dynamic == NULL means not a dynamically linked elf */ if (dynamic == NULL) { ret = EPKG_END; goto cleanup; /* not a dynamically linked elf: no results */ } if (!shlib_valid_abi(fpath, &elfhdr, myarch)) { ret = EPKG_END; goto cleanup; /* Invalid ABI */ } #ifdef __FreeBSD__ if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD && !is_old_freebsd_armheader(&elfhdr)) { ret = EPKG_END; goto cleanup; } #endif if ((data = elf_getdata(dynamic, NULL)) == NULL) { ret = EPKG_END; /* Some error occurred, ignore this file */ goto cleanup; } /* First, scan through the data from the .dynamic section to find any RPATH or RUNPATH settings. These are colon separated paths to prepend to the ld.so search paths from the ELF hints file. These always seem to come right after the NEEDED shared library entries. NEEDED entries should resolve to a filename for installed executables, but need not resolve for installed shared libraries -- additional info from the apps that link against them would be required. Shared libraries are distinguished by a DT_SONAME tag */ rpath_list_init(); for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag == DT_SONAME) { is_shlib = true; /* The file being scanned is a shared library *provided* by the package. Record this if appropriate */ shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); if (shlib != NULL && *shlib != '\0') pkg_addshlib_provided(pkg, shlib); } if ((dyn->d_tag == DT_RPATH || dyn->d_tag == DT_RUNPATH) && rpath == NULL) rpath = elf_strptr(e, sh_link, dyn->d_un.d_val); } if (rpath != NULL) shlib_list_from_rpath(rpath, bsd_dirname(fpath)); /* Now find all of the NEEDED shared libraries. */ for (dynidx = 0; dynidx < numdyn; dynidx++) { if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) { ret = EPKG_FATAL; pkg_emit_error("getdyn() failed for %s: %s", fpath, elf_errmsg(-1)); goto cleanup; } if (dyn->d_tag != DT_NEEDED) continue; shlib = elf_strptr(e, sh_link, dyn->d_un.d_val); add_shlibs_to_pkg(pkg, fpath, shlib, is_shlib); } cleanup: rpath_list_free(); if (e != NULL) elf_end(e); close(fd); return (ret); }