xref: /dragonfly/sbin/ldconfig/elfhints.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /*-
2*86d7f5d3SJohn Marino  * Copyright (c) 1998 John D. Polstra
3*86d7f5d3SJohn Marino  * All rights reserved.
4*86d7f5d3SJohn Marino  *
5*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
6*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
7*86d7f5d3SJohn Marino  * are met:
8*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
9*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
10*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
11*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
12*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
13*86d7f5d3SJohn Marino  *
14*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*86d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*86d7f5d3SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*86d7f5d3SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*86d7f5d3SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*86d7f5d3SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*86d7f5d3SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*86d7f5d3SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*86d7f5d3SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*86d7f5d3SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*86d7f5d3SJohn Marino  * SUCH DAMAGE.
25*86d7f5d3SJohn Marino  *
26*86d7f5d3SJohn Marino  * $FreeBSD: src/sbin/ldconfig/elfhints.c,v 1.3.2.3 2001/07/11 23:59:10 obrien Exp $
27*86d7f5d3SJohn Marino  * $DragonFly: src/sbin/ldconfig/elfhints.c,v 1.2 2003/06/17 04:27:33 dillon Exp $
28*86d7f5d3SJohn Marino  */
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino #include <sys/param.h>
31*86d7f5d3SJohn Marino #include <sys/mman.h>
32*86d7f5d3SJohn Marino #include <sys/stat.h>
33*86d7f5d3SJohn Marino 
34*86d7f5d3SJohn Marino #include <ctype.h>
35*86d7f5d3SJohn Marino #include <dirent.h>
36*86d7f5d3SJohn Marino #include <elf-hints.h>
37*86d7f5d3SJohn Marino #include <err.h>
38*86d7f5d3SJohn Marino #include <errno.h>
39*86d7f5d3SJohn Marino #include <fcntl.h>
40*86d7f5d3SJohn Marino #include <stdio.h>
41*86d7f5d3SJohn Marino #include <stdlib.h>
42*86d7f5d3SJohn Marino #include <string.h>
43*86d7f5d3SJohn Marino #include <unistd.h>
44*86d7f5d3SJohn Marino 
45*86d7f5d3SJohn Marino #include "ldconfig.h"
46*86d7f5d3SJohn Marino 
47*86d7f5d3SJohn Marino #define MAXDIRS		1024		/* Maximum directories in path */
48*86d7f5d3SJohn Marino #define MAXFILESIZE	(16*1024)	/* Maximum hints file size */
49*86d7f5d3SJohn Marino 
50*86d7f5d3SJohn Marino static void	add_dir(const char *, const char *, int);
51*86d7f5d3SJohn Marino static void	read_dirs_from_file(const char *, const char *);
52*86d7f5d3SJohn Marino static void	read_elf_hints(const char *, int);
53*86d7f5d3SJohn Marino static void	write_elf_hints(const char *);
54*86d7f5d3SJohn Marino 
55*86d7f5d3SJohn Marino static const char	*dirs[MAXDIRS];
56*86d7f5d3SJohn Marino static int		 ndirs;
57*86d7f5d3SJohn Marino int			 insecure;
58*86d7f5d3SJohn Marino 
59*86d7f5d3SJohn Marino static void
add_dir(const char * hintsfile,const char * name,int trusted)60*86d7f5d3SJohn Marino add_dir(const char *hintsfile, const char *name, int trusted)
61*86d7f5d3SJohn Marino {
62*86d7f5d3SJohn Marino 	struct stat 	stbuf;
63*86d7f5d3SJohn Marino 	int		i;
64*86d7f5d3SJohn Marino 
65*86d7f5d3SJohn Marino 	/* Do some security checks */
66*86d7f5d3SJohn Marino 	if (!trusted && !insecure) {
67*86d7f5d3SJohn Marino 		if (stat(name, &stbuf) == -1) {
68*86d7f5d3SJohn Marino 			warn("%s", name);
69*86d7f5d3SJohn Marino 			return;
70*86d7f5d3SJohn Marino 		}
71*86d7f5d3SJohn Marino 		if (stbuf.st_uid != 0) {
72*86d7f5d3SJohn Marino 			warnx("%s: ignoring directory not owned by root", name);
73*86d7f5d3SJohn Marino 			return;
74*86d7f5d3SJohn Marino 		}
75*86d7f5d3SJohn Marino 		if ((stbuf.st_mode & S_IWOTH) != 0) {
76*86d7f5d3SJohn Marino 			warnx("%s: ignoring world-writable directory", name);
77*86d7f5d3SJohn Marino 			return;
78*86d7f5d3SJohn Marino 		}
79*86d7f5d3SJohn Marino 		if ((stbuf.st_mode & S_IWGRP) != 0) {
80*86d7f5d3SJohn Marino 			warnx("%s: ignoring group-writable directory", name);
81*86d7f5d3SJohn Marino 			return;
82*86d7f5d3SJohn Marino 		}
83*86d7f5d3SJohn Marino 	}
84*86d7f5d3SJohn Marino 
85*86d7f5d3SJohn Marino 	for (i = 0;  i < ndirs;  i++)
86*86d7f5d3SJohn Marino 		if (strcmp(dirs[i], name) == 0)
87*86d7f5d3SJohn Marino 			return;
88*86d7f5d3SJohn Marino 	if (ndirs >= MAXDIRS)
89*86d7f5d3SJohn Marino 		errx(1, "\"%s\": Too many directories in path", hintsfile);
90*86d7f5d3SJohn Marino 	dirs[ndirs++] = name;
91*86d7f5d3SJohn Marino }
92*86d7f5d3SJohn Marino 
93*86d7f5d3SJohn Marino void
list_elf_hints(const char * hintsfile)94*86d7f5d3SJohn Marino list_elf_hints(const char *hintsfile)
95*86d7f5d3SJohn Marino {
96*86d7f5d3SJohn Marino 	int	i;
97*86d7f5d3SJohn Marino 	int	nlibs;
98*86d7f5d3SJohn Marino 
99*86d7f5d3SJohn Marino 	read_elf_hints(hintsfile, 1);
100*86d7f5d3SJohn Marino 	printf("%s:\n", hintsfile);
101*86d7f5d3SJohn Marino 	printf("\tsearch directories:");
102*86d7f5d3SJohn Marino 	for (i = 0;  i < ndirs;  i++)
103*86d7f5d3SJohn Marino 		printf("%c%s", i == 0 ? ' ' : ':', dirs[i]);
104*86d7f5d3SJohn Marino 	printf("\n");
105*86d7f5d3SJohn Marino 
106*86d7f5d3SJohn Marino 	nlibs = 0;
107*86d7f5d3SJohn Marino 	for (i = 0;  i < ndirs;  i++) {
108*86d7f5d3SJohn Marino 		DIR		*dirp;
109*86d7f5d3SJohn Marino 		struct dirent	*dp;
110*86d7f5d3SJohn Marino 
111*86d7f5d3SJohn Marino 		if ((dirp = opendir(dirs[i])) == NULL)
112*86d7f5d3SJohn Marino 			continue;
113*86d7f5d3SJohn Marino 		while ((dp = readdir(dirp)) != NULL) {
114*86d7f5d3SJohn Marino 			int		 len;
115*86d7f5d3SJohn Marino 			int		 namelen;
116*86d7f5d3SJohn Marino 			const char	*name;
117*86d7f5d3SJohn Marino 			const char	*vers;
118*86d7f5d3SJohn Marino 
119*86d7f5d3SJohn Marino 			/* Name can't be shorter than "libx.so.0" */
120*86d7f5d3SJohn Marino 			if ((len = strlen(dp->d_name)) < 9 ||
121*86d7f5d3SJohn Marino 			    strncmp(dp->d_name, "lib", 3) != 0)
122*86d7f5d3SJohn Marino 				continue;
123*86d7f5d3SJohn Marino 			name = dp->d_name + 3;
124*86d7f5d3SJohn Marino 			vers = dp->d_name + len;
125*86d7f5d3SJohn Marino 			while (vers > dp->d_name && isdigit(*(vers-1)))
126*86d7f5d3SJohn Marino 				vers--;
127*86d7f5d3SJohn Marino 			if (vers == dp->d_name + len)
128*86d7f5d3SJohn Marino 				continue;
129*86d7f5d3SJohn Marino 			if (vers < dp->d_name + 4 ||
130*86d7f5d3SJohn Marino 			    strncmp(vers - 4, ".so.", 4) != 0)
131*86d7f5d3SJohn Marino 				continue;
132*86d7f5d3SJohn Marino 
133*86d7f5d3SJohn Marino 			/* We have a valid shared library name. */
134*86d7f5d3SJohn Marino 			namelen = (vers - 4) - name;
135*86d7f5d3SJohn Marino 			printf("\t%d:-l%.*s.%s => %s/%s\n", nlibs,
136*86d7f5d3SJohn Marino 			    namelen, name, vers, dirs[i], dp->d_name);
137*86d7f5d3SJohn Marino 			nlibs++;
138*86d7f5d3SJohn Marino 		}
139*86d7f5d3SJohn Marino 		closedir(dirp);
140*86d7f5d3SJohn Marino 	}
141*86d7f5d3SJohn Marino }
142*86d7f5d3SJohn Marino 
143*86d7f5d3SJohn Marino static void
read_dirs_from_file(const char * hintsfile,const char * listfile)144*86d7f5d3SJohn Marino read_dirs_from_file(const char *hintsfile, const char *listfile)
145*86d7f5d3SJohn Marino {
146*86d7f5d3SJohn Marino 	FILE	*fp;
147*86d7f5d3SJohn Marino 	char	 buf[MAXPATHLEN];
148*86d7f5d3SJohn Marino 	int	 linenum;
149*86d7f5d3SJohn Marino 
150*86d7f5d3SJohn Marino 	if ((fp = fopen(listfile, "r")) == NULL)
151*86d7f5d3SJohn Marino 		err(1, "%s", listfile);
152*86d7f5d3SJohn Marino 
153*86d7f5d3SJohn Marino 	linenum = 0;
154*86d7f5d3SJohn Marino 	while (fgets(buf, sizeof buf, fp) != NULL) {
155*86d7f5d3SJohn Marino 		char	*cp, *sp;
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino 		linenum++;
158*86d7f5d3SJohn Marino 		cp = buf;
159*86d7f5d3SJohn Marino 		/* Skip leading white space. */
160*86d7f5d3SJohn Marino 		while (isspace(*cp))
161*86d7f5d3SJohn Marino 			cp++;
162*86d7f5d3SJohn Marino 		if (*cp == '#' || *cp == '\0')
163*86d7f5d3SJohn Marino 			continue;
164*86d7f5d3SJohn Marino 		sp = cp;
165*86d7f5d3SJohn Marino 		/* Advance over the directory name. */
166*86d7f5d3SJohn Marino 		while (!isspace(*cp) && *cp != '\0')
167*86d7f5d3SJohn Marino 			cp++;
168*86d7f5d3SJohn Marino 		/* Terminate the string and skip trailing white space. */
169*86d7f5d3SJohn Marino 		if (*cp != '\0') {
170*86d7f5d3SJohn Marino 			*cp++ = '\0';
171*86d7f5d3SJohn Marino 			while (isspace(*cp))
172*86d7f5d3SJohn Marino 				cp++;
173*86d7f5d3SJohn Marino 		}
174*86d7f5d3SJohn Marino 		/* Now we had better be at the end of the line. */
175*86d7f5d3SJohn Marino 		if (*cp != '\0')
176*86d7f5d3SJohn Marino 			warnx("%s:%d: trailing characters ignored",
177*86d7f5d3SJohn Marino 			    listfile, linenum);
178*86d7f5d3SJohn Marino 
179*86d7f5d3SJohn Marino 		if ((sp = strdup(sp)) == NULL)
180*86d7f5d3SJohn Marino 			errx(1, "Out of memory");
181*86d7f5d3SJohn Marino 		add_dir(hintsfile, sp, 0);
182*86d7f5d3SJohn Marino 	}
183*86d7f5d3SJohn Marino 
184*86d7f5d3SJohn Marino 	fclose(fp);
185*86d7f5d3SJohn Marino }
186*86d7f5d3SJohn Marino 
187*86d7f5d3SJohn Marino static void
read_elf_hints(const char * hintsfile,int must_exist)188*86d7f5d3SJohn Marino read_elf_hints(const char *hintsfile, int must_exist)
189*86d7f5d3SJohn Marino {
190*86d7f5d3SJohn Marino 	int	 		 fd;
191*86d7f5d3SJohn Marino 	struct stat		 s;
192*86d7f5d3SJohn Marino 	void			*mapbase;
193*86d7f5d3SJohn Marino 	struct elfhints_hdr	*hdr;
194*86d7f5d3SJohn Marino 	char			*strtab;
195*86d7f5d3SJohn Marino 	char			*dirlist;
196*86d7f5d3SJohn Marino 	char			*p;
197*86d7f5d3SJohn Marino 
198*86d7f5d3SJohn Marino 	if ((fd = open(hintsfile, O_RDONLY)) == -1) {
199*86d7f5d3SJohn Marino 		if (errno == ENOENT && !must_exist)
200*86d7f5d3SJohn Marino 			return;
201*86d7f5d3SJohn Marino 		err(1, "Cannot open \"%s\"", hintsfile);
202*86d7f5d3SJohn Marino 	}
203*86d7f5d3SJohn Marino 	if (fstat(fd, &s) == -1)
204*86d7f5d3SJohn Marino 		err(1, "Cannot stat \"%s\"", hintsfile);
205*86d7f5d3SJohn Marino 	if (s.st_size > MAXFILESIZE)
206*86d7f5d3SJohn Marino 		errx(1, "\"%s\" is unreasonably large", hintsfile);
207*86d7f5d3SJohn Marino 	/*
208*86d7f5d3SJohn Marino 	 * We use a read-write, private mapping so that we can null-terminate
209*86d7f5d3SJohn Marino 	 * some strings in it without affecting the underlying file.
210*86d7f5d3SJohn Marino 	 */
211*86d7f5d3SJohn Marino 	mapbase = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE,
212*86d7f5d3SJohn Marino 	    MAP_PRIVATE, fd, 0);
213*86d7f5d3SJohn Marino 	if (mapbase == MAP_FAILED)
214*86d7f5d3SJohn Marino 		err(1, "Cannot mmap \"%s\"", hintsfile);
215*86d7f5d3SJohn Marino 	close(fd);
216*86d7f5d3SJohn Marino 
217*86d7f5d3SJohn Marino 	hdr = (struct elfhints_hdr *)mapbase;
218*86d7f5d3SJohn Marino 	if (hdr->magic != ELFHINTS_MAGIC)
219*86d7f5d3SJohn Marino 		errx(1, "\"%s\": invalid file format", hintsfile);
220*86d7f5d3SJohn Marino 	if (hdr->version != 1)
221*86d7f5d3SJohn Marino 		errx(1, "\"%s\": unrecognized file version (%d)", hintsfile,
222*86d7f5d3SJohn Marino 		    hdr->version);
223*86d7f5d3SJohn Marino 
224*86d7f5d3SJohn Marino 	strtab = (char *)mapbase + hdr->strtab;
225*86d7f5d3SJohn Marino 	dirlist = strtab + hdr->dirlist;
226*86d7f5d3SJohn Marino 
227*86d7f5d3SJohn Marino 	if (*dirlist != '\0')
228*86d7f5d3SJohn Marino 		while ((p = strsep(&dirlist, ":")) != NULL)
229*86d7f5d3SJohn Marino 			add_dir(hintsfile, p, 1);
230*86d7f5d3SJohn Marino }
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino void
update_elf_hints(const char * hintsfile,int argc,char ** argv,int merge)233*86d7f5d3SJohn Marino update_elf_hints(const char *hintsfile, int argc, char **argv, int merge)
234*86d7f5d3SJohn Marino {
235*86d7f5d3SJohn Marino 	int	i;
236*86d7f5d3SJohn Marino 
237*86d7f5d3SJohn Marino 	if (merge)
238*86d7f5d3SJohn Marino 		read_elf_hints(hintsfile, 0);
239*86d7f5d3SJohn Marino 	for (i = 0;  i < argc;  i++) {
240*86d7f5d3SJohn Marino 		struct stat	s;
241*86d7f5d3SJohn Marino 
242*86d7f5d3SJohn Marino 		if (stat(argv[i], &s) == -1)
243*86d7f5d3SJohn Marino 			warn("warning: %s", argv[i]);
244*86d7f5d3SJohn Marino 		else if (S_ISREG(s.st_mode))
245*86d7f5d3SJohn Marino 			read_dirs_from_file(hintsfile, argv[i]);
246*86d7f5d3SJohn Marino 		else
247*86d7f5d3SJohn Marino 			add_dir(hintsfile, argv[i], 0);
248*86d7f5d3SJohn Marino 	}
249*86d7f5d3SJohn Marino 	write_elf_hints(hintsfile);
250*86d7f5d3SJohn Marino }
251*86d7f5d3SJohn Marino 
252*86d7f5d3SJohn Marino static void
write_elf_hints(const char * hintsfile)253*86d7f5d3SJohn Marino write_elf_hints(const char *hintsfile)
254*86d7f5d3SJohn Marino {
255*86d7f5d3SJohn Marino 	struct elfhints_hdr	 hdr;
256*86d7f5d3SJohn Marino 	char			*tempname;
257*86d7f5d3SJohn Marino 	int			 fd;
258*86d7f5d3SJohn Marino 	FILE			*fp;
259*86d7f5d3SJohn Marino 	int			 i;
260*86d7f5d3SJohn Marino 
261*86d7f5d3SJohn Marino 	if (asprintf(&tempname, "%s.XXXXXX", hintsfile) == -1)
262*86d7f5d3SJohn Marino 		errx(1, "Out of memory");
263*86d7f5d3SJohn Marino 	if ((fd = mkstemp(tempname)) ==  -1)
264*86d7f5d3SJohn Marino 		err(1, "mkstemp(%s)", tempname);
265*86d7f5d3SJohn Marino 	if (fchmod(fd, 0444) == -1)
266*86d7f5d3SJohn Marino 		err(1, "fchmod(%s)", tempname);
267*86d7f5d3SJohn Marino 	if ((fp = fdopen(fd, "wb")) == NULL)
268*86d7f5d3SJohn Marino 		err(1, "fdopen(%s)", tempname);
269*86d7f5d3SJohn Marino 
270*86d7f5d3SJohn Marino 	hdr.magic = ELFHINTS_MAGIC;
271*86d7f5d3SJohn Marino 	hdr.version = 1;
272*86d7f5d3SJohn Marino 	hdr.strtab = sizeof hdr;
273*86d7f5d3SJohn Marino 	hdr.strsize = 0;
274*86d7f5d3SJohn Marino 	hdr.dirlist = 0;
275*86d7f5d3SJohn Marino 	memset(hdr.spare, 0, sizeof hdr.spare);
276*86d7f5d3SJohn Marino 
277*86d7f5d3SJohn Marino 	/* Count up the size of the string table. */
278*86d7f5d3SJohn Marino 	if (ndirs > 0) {
279*86d7f5d3SJohn Marino 		hdr.strsize += strlen(dirs[0]);
280*86d7f5d3SJohn Marino 		for (i = 1;  i < ndirs;  i++)
281*86d7f5d3SJohn Marino 			hdr.strsize += 1 + strlen(dirs[i]);
282*86d7f5d3SJohn Marino 	}
283*86d7f5d3SJohn Marino 	hdr.dirlistlen = hdr.strsize;
284*86d7f5d3SJohn Marino 	hdr.strsize++;	/* For the null terminator */
285*86d7f5d3SJohn Marino 
286*86d7f5d3SJohn Marino 	/* Write the header. */
287*86d7f5d3SJohn Marino 	if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr)
288*86d7f5d3SJohn Marino 		err(1, "%s: write error", tempname);
289*86d7f5d3SJohn Marino 	/* Write the strings. */
290*86d7f5d3SJohn Marino 	if (ndirs > 0) {
291*86d7f5d3SJohn Marino 		if (fputs(dirs[0], fp) == EOF)
292*86d7f5d3SJohn Marino 			err(1, "%s: write error", tempname);
293*86d7f5d3SJohn Marino 		for (i = 1;  i < ndirs;  i++)
294*86d7f5d3SJohn Marino 			if (fprintf(fp, ":%s", dirs[i]) < 0)
295*86d7f5d3SJohn Marino 				err(1, "%s: write error", tempname);
296*86d7f5d3SJohn Marino 	}
297*86d7f5d3SJohn Marino 	if (putc('\0', fp) == EOF || fclose(fp) == EOF)
298*86d7f5d3SJohn Marino 		err(1, "%s: write error", tempname);
299*86d7f5d3SJohn Marino 
300*86d7f5d3SJohn Marino 	if (rename(tempname, hintsfile) == -1)
301*86d7f5d3SJohn Marino 		err(1, "rename %s to %s", tempname, hintsfile);
302*86d7f5d3SJohn Marino 	free(tempname);
303*86d7f5d3SJohn Marino }
304