xref: /openbsd/libexec/ld.so/ldconfig/sod.c (revision 3cab2bb3)
1 /*	$OpenBSD: sod.c,v 1.7 2015/10/29 13:07:41 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1993 Paul Kranenburg
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Paul Kranenburg.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/mman.h>
37 #include <machine/exec.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <fcntl.h>
41 #include <nlist.h>
42 #include <link.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 
47 #if 0
48 #include "syscall.h"
49 #include "archdep.h"
50 #include "util.h"
51 #endif
52 #include "path.h"
53 #include "sod.h"
54 
55 int _dl_hinthash(char *cp, int vmajor, int vminor);
56 void _dl_maphints(void);
57 
58 /*
59  * Populate sod struct for dlopen's call to map_object
60  */
61 void
62 _dl_build_sod(const char *name, struct sod *sodp)
63 {
64 	unsigned int	tuplet;
65 	int		major, minor;
66 	char		*realname, *tok, *etok, *cp;
67 
68 	/* default is an absolute or relative path */
69 	sodp->sod_name = (long)strdup(name);    /* strtok is destructive */
70 	if (sodp->sod_name == 0)
71 		exit(7);
72 	sodp->sod_library = 0;
73 	sodp->sod_major = sodp->sod_minor = 0;
74 
75 	/* does it look like /^lib/ ? */
76 	if (strncmp((char *)sodp->sod_name, "lib", 3) != 0)
77 		goto backout;
78 
79 	/* is this a filename? */
80 	if (strchr((char *)sodp->sod_name, '/'))
81 		goto backout;
82 
83 	/* skip over 'lib' */
84 	cp = (char *)sodp->sod_name + 3;
85 
86 	realname = cp;
87 
88 	/* dot guardian */
89 	if ((strchr(cp, '.') == NULL) || (*(cp+strlen(cp)-1) == '.'))
90 		goto backout;
91 
92 	cp = strstr(cp, ".so");
93 	if (cp == NULL)
94 		goto backout;
95 
96 	/* default */
97 	major = minor = -1;
98 
99 	/* loop through name - parse skipping name */
100 	for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
101 		switch (tuplet) {
102 		case 0:
103 			/* empty tok, we already skipped to "\.so.*" */
104 			break;
105 		case 1:
106 			/* 'so' extension */
107 			break;
108 		case 2:
109 			/* major version extension */
110 			major = strtol(tok, &etok, 10);
111 			if (*tok == '\0' || *etok != '\0')
112 				goto backout;
113 			break;
114 		case 3:
115 			/* minor version extension */
116 			minor = strtol(tok, &etok, 10);
117 			if (*tok == '\0' || *etok != '\0')
118 				goto backout;
119 			break;
120 		/* if we get here, it must be weird */
121 		default:
122 			goto backout;
123 		}
124 	}
125 	if (realname == NULL)
126 		goto backout;
127 	cp = (char *)sodp->sod_name;
128 	sodp->sod_name = (long)strdup(realname);
129 	if (sodp->sod_name == 0)
130 		exit(7);
131 	free(cp);
132 	sodp->sod_library = 1;
133 	sodp->sod_major = major;
134 	sodp->sod_minor = minor;
135 	return;
136 
137 backout:
138 	free((char *)sodp->sod_name);
139 	sodp->sod_name = (long)strdup(name);
140 	if (sodp->sod_name == 0)
141 		exit(7);
142 }
143 
144 static struct hints_header	*hheader = NULL;
145 static struct hints_bucket	*hbuckets;
146 static char			*hstrtab;
147 char				**_dl_hint_search_path = NULL;
148 
149 #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
150 
151 void
152 _dl_maphints(void)
153 {
154 	struct stat	sb;
155 	caddr_t		addr = MAP_FAILED;
156 	long		hsize = 0;
157 	int		hfd;
158 
159 	if ((hfd = open(_PATH_LD_HINTS, O_RDONLY)) < 0)
160 		goto bad_hints;
161 
162 	if (fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
163 	    sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
164 		goto bad_hints;
165 
166 	hsize = (long)sb.st_size;
167 	addr = (void *)mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
168 	if (addr == MAP_FAILED)
169 		goto bad_hints;
170 
171 	hheader = (struct hints_header *)addr;
172 	if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
173 		goto bad_hints;
174 
175 	if (hheader->hh_version != LD_HINTS_VERSION_2)
176 		goto bad_hints;
177 
178 	hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
179 	hstrtab = (char *)(addr + hheader->hh_strtab);
180 	if (hheader->hh_version >= LD_HINTS_VERSION_2)
181 		_dl_hint_search_path = _dl_split_path(hstrtab + hheader->hh_dirlist);
182 
183 	/* close the file descriptor, leaving the hints mapped */
184 	close(hfd);
185 
186 	return;
187 
188 bad_hints:
189 	if (addr != MAP_FAILED)
190 		munmap(addr, hsize);
191 	if (hfd != -1)
192 		close(hfd);
193 	hheader = (struct hints_header *)-1;
194 }
195 
196 char *
197 _dl_findhint(char *name, int major, int minor, char *preferred_path)
198 {
199 	struct hints_bucket	*bp;
200 
201 	/*
202 	 * If not mapped, and we have not tried before, try to map the
203 	 * hints, if previous attempts failed hheader is -1 and we
204 	 * do not wish to retry it.
205 	 */
206 	if (hheader == NULL)
207 		_dl_maphints();
208 
209 	/* if it failed to map, return failure */
210 	if (!(HINTS_VALID))
211 		return NULL;
212 
213 	if (hheader->hh_nbucket == 0)
214 		return NULL;
215 
216 	bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
217 
218 	while (1) {
219 		/* Sanity check */
220 		if (bp->hi_namex >= hheader->hh_strtab_sz) {
221 			printf("Bad name index: %#x\n", bp->hi_namex);
222 			exit(7);
223 			break;
224 		}
225 		if (bp->hi_pathx >= hheader->hh_strtab_sz) {
226 			printf("Bad path index: %#x\n", bp->hi_pathx);
227 			exit(7);
228 			break;
229 		}
230 
231 		if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
232 			/* It's `name', check version numbers */
233 			if (bp->hi_major == major &&
234 			    (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
235 				if (preferred_path == NULL) {
236 					return hstrtab + bp->hi_pathx;
237 				} else {
238 					char *path = hstrtab + bp->hi_pathx;
239 					char *edir = strrchr(path, '/');
240 
241 					if ((strncmp(preferred_path, path,
242 					    (edir - path)) == 0) &&
243 					    (preferred_path[edir - path] == '\0'))
244 						return path;
245 				}
246 			}
247 		}
248 
249 		if (bp->hi_next == -1)
250 			break;
251 
252 		/* Move on to next in bucket */
253 		bp = &hbuckets[bp->hi_next];
254 	}
255 
256 	/* No hints available for name */
257 	return NULL;
258 }
259 
260 int
261 _dl_hinthash(char *cp, int vmajor, int vminor)
262 {
263 	int	k = 0;
264 
265 	while (*cp)
266 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
267 
268 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
269 
270 	return k;
271 }
272