138836Sjdp/*- 238836Sjdp * Copyright (c) 1998 John D. Polstra 338836Sjdp * All rights reserved. 438836Sjdp * 538836Sjdp * Redistribution and use in source and binary forms, with or without 638836Sjdp * modification, are permitted provided that the following conditions 738836Sjdp * are met: 838836Sjdp * 1. Redistributions of source code must retain the above copyright 938836Sjdp * notice, this list of conditions and the following disclaimer. 1038836Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1138836Sjdp * notice, this list of conditions and the following disclaimer in the 1238836Sjdp * documentation and/or other materials provided with the distribution. 1338836Sjdp * 1438836Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538836Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638836Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738836Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838836Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938836Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038836Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138836Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238836Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338836Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438836Sjdp * SUCH DAMAGE. 2538836Sjdp * 2650476Speter * $FreeBSD$ 2738836Sjdp */ 2838836Sjdp 2938836Sjdp#include <sys/param.h> 3038836Sjdp#include <sys/mman.h> 3138836Sjdp#include <sys/stat.h> 3238836Sjdp 3338836Sjdp#include <ctype.h> 3438836Sjdp#include <dirent.h> 3576224Sobrien#include <elf-hints.h> 3638836Sjdp#include <err.h> 3738836Sjdp#include <errno.h> 3838836Sjdp#include <fcntl.h> 3938836Sjdp#include <stdio.h> 4038836Sjdp#include <stdlib.h> 4138836Sjdp#include <string.h> 4238836Sjdp#include <unistd.h> 4338836Sjdp 4438836Sjdp#include "ldconfig.h" 4538836Sjdp 4638836Sjdp#define MAXDIRS 1024 /* Maximum directories in path */ 4738836Sjdp#define MAXFILESIZE (16*1024) /* Maximum hints file size */ 4838836Sjdp 4964360Sjdpstatic void add_dir(const char *, const char *, int); 5038836Sjdpstatic void read_dirs_from_file(const char *, const char *); 5138836Sjdpstatic void read_elf_hints(const char *, int); 5238836Sjdpstatic void write_elf_hints(const char *); 5338836Sjdp 5438836Sjdpstatic const char *dirs[MAXDIRS]; 5538836Sjdpstatic int ndirs; 5672923Spsint insecure; 5738836Sjdp 5838836Sjdpstatic void 5964360Sjdpadd_dir(const char *hintsfile, const char *name, int trusted) 6038836Sjdp{ 6163872Sjdp struct stat stbuf; 6263872Sjdp int i; 6338836Sjdp 6463872Sjdp /* Do some security checks */ 6564360Sjdp if (!trusted && !insecure) { 6664360Sjdp if (stat(name, &stbuf) == -1) { 6764360Sjdp warn("%s", name); 6864360Sjdp return; 6964360Sjdp } 7064360Sjdp if (stbuf.st_uid != 0) { 7164360Sjdp warnx("%s: ignoring directory not owned by root", name); 7264360Sjdp return; 7364360Sjdp } 7464360Sjdp if ((stbuf.st_mode & S_IWOTH) != 0) { 7564360Sjdp warnx("%s: ignoring world-writable directory", name); 7664360Sjdp return; 7764360Sjdp } 7870121Sjdp if ((stbuf.st_mode & S_IWGRP) != 0) { 7970121Sjdp warnx("%s: ignoring group-writable directory", name); 8070121Sjdp return; 8170121Sjdp } 8263872Sjdp } 8363872Sjdp 8438836Sjdp for (i = 0; i < ndirs; i++) 8538836Sjdp if (strcmp(dirs[i], name) == 0) 8638836Sjdp return; 8738836Sjdp if (ndirs >= MAXDIRS) 8838836Sjdp errx(1, "\"%s\": Too many directories in path", hintsfile); 8938836Sjdp dirs[ndirs++] = name; 9038836Sjdp} 9138836Sjdp 9238836Sjdpvoid 9338836Sjdplist_elf_hints(const char *hintsfile) 9438836Sjdp{ 9538836Sjdp int i; 9638836Sjdp int nlibs; 9738836Sjdp 9838836Sjdp read_elf_hints(hintsfile, 1); 9938836Sjdp printf("%s:\n", hintsfile); 10038836Sjdp printf("\tsearch directories:"); 10138836Sjdp for (i = 0; i < ndirs; i++) 10238836Sjdp printf("%c%s", i == 0 ? ' ' : ':', dirs[i]); 10338836Sjdp printf("\n"); 10438836Sjdp 10538836Sjdp nlibs = 0; 10638836Sjdp for (i = 0; i < ndirs; i++) { 10738836Sjdp DIR *dirp; 10838836Sjdp struct dirent *dp; 10938836Sjdp 11038836Sjdp if ((dirp = opendir(dirs[i])) == NULL) 11138836Sjdp continue; 11238836Sjdp while ((dp = readdir(dirp)) != NULL) { 11338836Sjdp int len; 11438836Sjdp int namelen; 11538836Sjdp const char *name; 11638836Sjdp const char *vers; 11738836Sjdp 11838836Sjdp /* Name can't be shorter than "libx.so.0" */ 11938836Sjdp if ((len = strlen(dp->d_name)) < 9 || 12038836Sjdp strncmp(dp->d_name, "lib", 3) != 0) 12138836Sjdp continue; 12238836Sjdp name = dp->d_name + 3; 12338836Sjdp vers = dp->d_name + len; 12438836Sjdp while (vers > dp->d_name && isdigit(*(vers-1))) 12538836Sjdp vers--; 12638836Sjdp if (vers == dp->d_name + len) 12738836Sjdp continue; 12838836Sjdp if (vers < dp->d_name + 4 || 12938836Sjdp strncmp(vers - 4, ".so.", 4) != 0) 13038836Sjdp continue; 13138836Sjdp 13238836Sjdp /* We have a valid shared library name. */ 13338836Sjdp namelen = (vers - 4) - name; 13438836Sjdp printf("\t%d:-l%.*s.%s => %s/%s\n", nlibs, 13538836Sjdp namelen, name, vers, dirs[i], dp->d_name); 13638836Sjdp nlibs++; 13738836Sjdp } 13838836Sjdp closedir(dirp); 13938836Sjdp } 14038836Sjdp} 14138836Sjdp 14238836Sjdpstatic void 14338836Sjdpread_dirs_from_file(const char *hintsfile, const char *listfile) 14438836Sjdp{ 14538836Sjdp FILE *fp; 14638836Sjdp char buf[MAXPATHLEN]; 14738836Sjdp int linenum; 14838836Sjdp 14938836Sjdp if ((fp = fopen(listfile, "r")) == NULL) 15038836Sjdp err(1, "%s", listfile); 15138836Sjdp 15238836Sjdp linenum = 0; 15338836Sjdp while (fgets(buf, sizeof buf, fp) != NULL) { 15438836Sjdp char *cp, *sp; 15538836Sjdp 15638836Sjdp linenum++; 15738836Sjdp cp = buf; 15838836Sjdp /* Skip leading white space. */ 15938836Sjdp while (isspace(*cp)) 16038836Sjdp cp++; 16138836Sjdp if (*cp == '#' || *cp == '\0') 16238836Sjdp continue; 16338836Sjdp sp = cp; 16438836Sjdp /* Advance over the directory name. */ 16538836Sjdp while (!isspace(*cp) && *cp != '\0') 16638836Sjdp cp++; 16738836Sjdp /* Terminate the string and skip trailing white space. */ 16838836Sjdp if (*cp != '\0') { 16938836Sjdp *cp++ = '\0'; 17038836Sjdp while (isspace(*cp)) 17138836Sjdp cp++; 17238836Sjdp } 17338836Sjdp /* Now we had better be at the end of the line. */ 17438836Sjdp if (*cp != '\0') 17538836Sjdp warnx("%s:%d: trailing characters ignored", 17638836Sjdp listfile, linenum); 17738836Sjdp 17838836Sjdp if ((sp = strdup(sp)) == NULL) 17938836Sjdp errx(1, "Out of memory"); 18064360Sjdp add_dir(hintsfile, sp, 0); 18138836Sjdp } 18238836Sjdp 18338836Sjdp fclose(fp); 18438836Sjdp} 18538836Sjdp 18638836Sjdpstatic void 18738836Sjdpread_elf_hints(const char *hintsfile, int must_exist) 18838836Sjdp{ 18938836Sjdp int fd; 19038836Sjdp struct stat s; 19138836Sjdp void *mapbase; 19238836Sjdp struct elfhints_hdr *hdr; 19338836Sjdp char *strtab; 19438836Sjdp char *dirlist; 19538836Sjdp char *p; 19638836Sjdp 19738836Sjdp if ((fd = open(hintsfile, O_RDONLY)) == -1) { 19838836Sjdp if (errno == ENOENT && !must_exist) 19938836Sjdp return; 20038836Sjdp err(1, "Cannot open \"%s\"", hintsfile); 20138836Sjdp } 20238836Sjdp if (fstat(fd, &s) == -1) 20338836Sjdp err(1, "Cannot stat \"%s\"", hintsfile); 20438836Sjdp if (s.st_size > MAXFILESIZE) 20538836Sjdp errx(1, "\"%s\" is unreasonably large", hintsfile); 20638836Sjdp /* 20738836Sjdp * We use a read-write, private mapping so that we can null-terminate 20838836Sjdp * some strings in it without affecting the underlying file. 20938836Sjdp */ 21038836Sjdp mapbase = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE, 21138836Sjdp MAP_PRIVATE, fd, 0); 21238836Sjdp if (mapbase == MAP_FAILED) 21338836Sjdp err(1, "Cannot mmap \"%s\"", hintsfile); 21438836Sjdp close(fd); 21538836Sjdp 21638836Sjdp hdr = (struct elfhints_hdr *)mapbase; 21738836Sjdp if (hdr->magic != ELFHINTS_MAGIC) 21838836Sjdp errx(1, "\"%s\": invalid file format", hintsfile); 21938836Sjdp if (hdr->version != 1) 22038836Sjdp errx(1, "\"%s\": unrecognized file version (%d)", hintsfile, 22138836Sjdp hdr->version); 22238836Sjdp 22338836Sjdp strtab = (char *)mapbase + hdr->strtab; 22438836Sjdp dirlist = strtab + hdr->dirlist; 22538836Sjdp 22638836Sjdp if (*dirlist != '\0') 22738836Sjdp while ((p = strsep(&dirlist, ":")) != NULL) 22864360Sjdp add_dir(hintsfile, p, 1); 22938836Sjdp} 23038836Sjdp 23138836Sjdpvoid 23238836Sjdpupdate_elf_hints(const char *hintsfile, int argc, char **argv, int merge) 23338836Sjdp{ 23438836Sjdp int i; 23538836Sjdp 23638836Sjdp if (merge) 23738836Sjdp read_elf_hints(hintsfile, 0); 23838836Sjdp for (i = 0; i < argc; i++) { 23938836Sjdp struct stat s; 24038836Sjdp 24138836Sjdp if (stat(argv[i], &s) == -1) 24256357Sjdp warn("warning: %s", argv[i]); 24356357Sjdp else if (S_ISREG(s.st_mode)) 24438836Sjdp read_dirs_from_file(hintsfile, argv[i]); 24538836Sjdp else 24664360Sjdp add_dir(hintsfile, argv[i], 0); 24738836Sjdp } 24838836Sjdp write_elf_hints(hintsfile); 24938836Sjdp} 25038836Sjdp 25138836Sjdpstatic void 25238836Sjdpwrite_elf_hints(const char *hintsfile) 25338836Sjdp{ 25438836Sjdp struct elfhints_hdr hdr; 25538836Sjdp char *tempname; 25638836Sjdp int fd; 25738836Sjdp FILE *fp; 25838836Sjdp int i; 25938836Sjdp 26038836Sjdp if (asprintf(&tempname, "%s.XXXXXX", hintsfile) == -1) 26138836Sjdp errx(1, "Out of memory"); 26238836Sjdp if ((fd = mkstemp(tempname)) == -1) 26338836Sjdp err(1, "mkstemp(%s)", tempname); 26438836Sjdp if (fchmod(fd, 0444) == -1) 26538836Sjdp err(1, "fchmod(%s)", tempname); 26638836Sjdp if ((fp = fdopen(fd, "wb")) == NULL) 26738836Sjdp err(1, "fdopen(%s)", tempname); 26838836Sjdp 26938836Sjdp hdr.magic = ELFHINTS_MAGIC; 27038836Sjdp hdr.version = 1; 27138836Sjdp hdr.strtab = sizeof hdr; 27238836Sjdp hdr.strsize = 0; 27338836Sjdp hdr.dirlist = 0; 27438836Sjdp memset(hdr.spare, 0, sizeof hdr.spare); 27538836Sjdp 27638836Sjdp /* Count up the size of the string table. */ 27738836Sjdp if (ndirs > 0) { 27838836Sjdp hdr.strsize += strlen(dirs[0]); 27938836Sjdp for (i = 1; i < ndirs; i++) 28038836Sjdp hdr.strsize += 1 + strlen(dirs[i]); 28138836Sjdp } 28238836Sjdp hdr.dirlistlen = hdr.strsize; 28338836Sjdp hdr.strsize++; /* For the null terminator */ 28438836Sjdp 28538836Sjdp /* Write the header. */ 28638836Sjdp if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr) 28738836Sjdp err(1, "%s: write error", tempname); 28838836Sjdp /* Write the strings. */ 28938836Sjdp if (ndirs > 0) { 29038836Sjdp if (fputs(dirs[0], fp) == EOF) 29138836Sjdp err(1, "%s: write error", tempname); 29238836Sjdp for (i = 1; i < ndirs; i++) 29338836Sjdp if (fprintf(fp, ":%s", dirs[i]) < 0) 29438836Sjdp err(1, "%s: write error", tempname); 29538836Sjdp } 29638836Sjdp if (putc('\0', fp) == EOF || fclose(fp) == EOF) 29738836Sjdp err(1, "%s: write error", tempname); 29838836Sjdp 29938836Sjdp if (rename(tempname, hintsfile) == -1) 30038836Sjdp err(1, "rename %s to %s", tempname, hintsfile); 30138836Sjdp free(tempname); 30238836Sjdp} 303