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