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