1 /*	$OpenBSD: bootparamd.c,v 1.21 2016/10/16 10:40:58 jca Exp $	*/
2 
3 /*
4  * This code is not copyright, and is placed in the public domain.
5  * Feel free to use and modify. Please send modifications and/or
6  * suggestions + bug fixes to Klas Heggemann <klas@nada.kth.se>
7  *
8  * Various small changes by Theo de Raadt <deraadt@fsa.ca>
9  */
10 
11 #include <sys/types.h>
12 #include <sys/ioctl.h>
13 #include <sys/stat.h>
14 #include <sys/socket.h>
15 
16 #include <rpc/rpc.h>
17 #include <rpcsvc/bootparam_prot.h>
18 #include <rpcsvc/ypclnt.h>
19 #include <rpcsvc/yp_prot.h>
20 #include <arpa/inet.h>
21 
22 #include <stdio.h>
23 #include <netdb.h>
24 #include <ctype.h>
25 #include <syslog.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <stdlib.h>
30 
31 #include "pathnames.h"
32 
33 #define MAXLEN 800
34 
35 struct hostent *he;
36 static char hostname[MAX_MACHINE_NAME];
37 static char askname[MAX_MACHINE_NAME];
38 static char domain_name[MAX_MACHINE_NAME];
39 
40 extern void bootparamprog_1(struct svc_req *, SVCXPRT *);
41 int lookup_bootparam(char *client, char *client_canonical, char *id,
42     char **server, char **path);
43 
44 int	_rpcsvcdirty = 0;
45 int	_rpcpmstart = 0;
46 int	debug = 0;
47 int	dolog = 0;
48 struct in_addr route_addr;
49 struct sockaddr_in my_addr;
50 extern char *__progname;
51 char   *bootpfile = _PATH_BOOTPARAMS;
52 
53 extern char *optarg;
54 extern int optind;
55 
56 static void
57 usage(void)
58 {
59 	extern char *__progname;
60 	fprintf(stderr, "usage: %s [-ds] [-f file] [-r router]\n",
61 	    __progname);
62 	exit(1);
63 }
64 
65 /*
66  * ever familiar
67  */
68 int
69 main(int argc, char *argv[])
70 {
71 	struct hostent *he;
72 	struct stat buf;
73 	SVCXPRT *transp;
74 	int    c;
75 
76 	while ((c = getopt(argc, argv, "dsr:f:")) != -1)
77 		switch (c) {
78 		case 'd':
79 			debug = 1;
80 			break;
81 		case 'r':
82 			if (inet_aton(optarg, &route_addr) == 1)
83 				break;
84 			he = gethostbyname(optarg);
85 			if (!he) {
86 				warnx("no such host: %s", optarg);
87 				usage();
88 			}
89 			bcopy(he->h_addr, &route_addr.s_addr,
90 			    sizeof(route_addr.s_addr));
91 			break;
92 		case 'f':
93 			bootpfile = optarg;
94 			break;
95 		case 's':
96 			dolog = 1;
97 #ifndef LOG_DAEMON
98 			openlog(__progname, 0, 0);
99 #else
100 			openlog(__progname, 0, LOG_DAEMON);
101 			setlogmask(LOG_UPTO(LOG_NOTICE));
102 #endif
103 			break;
104 		default:
105 			usage();
106 		}
107 
108 	if (stat(bootpfile, &buf))
109 		err(1, "%s", bootpfile);
110 
111 	if (!route_addr.s_addr) {
112 		get_myaddress(&my_addr);
113 		bcopy(&my_addr.sin_addr.s_addr, &route_addr.s_addr,
114 		    sizeof(route_addr.s_addr));
115 	}
116 	if (!debug) {
117 		if (daemon(0, 0))
118 			err(1, "can't detach from terminal");
119 	}
120 
121 	(void) pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS);
122 
123 	transp = svcudp_create(RPC_ANYSOCK);
124 	if (transp == NULL)
125 		errx(1, "can't create udp service");
126 
127 	if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1,
128 	    IPPROTO_UDP))
129 		errx(1, "unable to register BOOTPARAMPROG version %ld, udp",
130 		    BOOTPARAMVERS);
131 
132 	if (pledge("stdio rpath dns", NULL) == -1)
133 		err(1, "pledge");
134 
135 	svc_run();
136 	errx(1, "svc_run returned");
137 }
138 
139 bp_whoami_res *
140 bootparamproc_whoami_1_svc(bp_whoami_arg *whoami, struct svc_req *rqstp)
141 {
142 	in_addr_t haddr;
143 	static bp_whoami_res res;
144 
145 	if (debug)
146 		warnx("whoami got question for %d.%d.%d.%d",
147 		    255 & whoami->client_address.bp_address_u.ip_addr.net,
148 		    255 & whoami->client_address.bp_address_u.ip_addr.host,
149 		    255 & whoami->client_address.bp_address_u.ip_addr.lh,
150 		    255 & whoami->client_address.bp_address_u.ip_addr.impno);
151 	if (dolog)
152 		syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d",
153 		    255 & whoami->client_address.bp_address_u.ip_addr.net,
154 		    255 & whoami->client_address.bp_address_u.ip_addr.host,
155 		    255 & whoami->client_address.bp_address_u.ip_addr.lh,
156 		    255 & whoami->client_address.bp_address_u.ip_addr.impno);
157 
158 	bcopy(&whoami->client_address.bp_address_u.ip_addr,
159 	    &haddr, sizeof(haddr));
160 	he = gethostbyaddr(&haddr, sizeof(haddr), AF_INET);
161 	if (!he)
162 		goto failed;
163 
164 	if (debug)
165 		warnx("This is host %s", he->h_name);
166 	if (dolog)
167 		syslog(LOG_NOTICE, "This is host %s", he->h_name);
168 
169 	strlcpy(askname, he->h_name, sizeof askname);
170 	if (!lookup_bootparam(askname, hostname, NULL, NULL, NULL)) {
171 		res.client_name = hostname;
172 		getdomainname(domain_name, MAX_MACHINE_NAME);
173 		res.domain_name = domain_name;
174 
175 		if (res.router_address.address_type != IP_ADDR_TYPE) {
176 			res.router_address.address_type = IP_ADDR_TYPE;
177 			bcopy(&route_addr.s_addr,
178 			    &res.router_address.bp_address_u.ip_addr, 4);
179 		}
180 		if (debug)
181 			warnx("Returning %s   %s    %d.%d.%d.%d",
182 			    res.client_name, res.domain_name,
183 			    255 & res.router_address.bp_address_u.ip_addr.net,
184 			    255 & res.router_address.bp_address_u.ip_addr.host,
185 			    255 & res.router_address.bp_address_u.ip_addr.lh,
186 			    255 & res.router_address.bp_address_u.ip_addr.impno);
187 		if (dolog)
188 			syslog(LOG_NOTICE, "Returning %s   %s    %d.%d.%d.%d",
189 			    res.client_name, res.domain_name,
190 			    255 & res.router_address.bp_address_u.ip_addr.net,
191 			    255 & res.router_address.bp_address_u.ip_addr.host,
192 			    255 & res.router_address.bp_address_u.ip_addr.lh,
193 			    255 & res.router_address.bp_address_u.ip_addr.impno);
194 		return (&res);
195 	}
196 failed:
197 	if (debug)
198 		warnx("whoami failed");
199 	if (dolog)
200 		syslog(LOG_NOTICE, "whoami failed");
201 	return (NULL);
202 }
203 
204 
205 bp_getfile_res *
206 bootparamproc_getfile_1_svc(bp_getfile_arg *getfile, struct svc_req *rqstp)
207 {
208 	static bp_getfile_res res;
209 	int error;
210 
211 	if (debug)
212 		warnx("getfile got question for \"%s\" and file \"%s\"",
213 		    getfile->client_name, getfile->file_id);
214 
215 	if (dolog)
216 		syslog(LOG_NOTICE,
217 		    "getfile got question for \"%s\" and file \"%s\"",
218 		    getfile->client_name, getfile->file_id);
219 
220 	he = NULL;
221 	he = gethostbyname(getfile->client_name);
222 	if (!he)
223 		goto failed;
224 
225 	strlcpy(askname, he->h_name, sizeof askname);
226 	error = lookup_bootparam(askname, NULL, getfile->file_id,
227 	    &res.server_name, &res.server_path);
228 	if (error == 0) {
229 		he = gethostbyname(res.server_name);
230 		if (!he)
231 			goto failed;
232 		bcopy(he->h_addr, &res.server_address.bp_address_u.ip_addr, 4);
233 		res.server_address.address_type = IP_ADDR_TYPE;
234 	} else if (error == ENOENT && !strcmp(getfile->file_id, "dump")) {
235 		/* Special for dump, answer with null strings. */
236 		res.server_name[0] = '\0';
237 		res.server_path[0] = '\0';
238 		bzero(&res.server_address.bp_address_u.ip_addr, 4);
239 	} else {
240 failed:
241 		if (debug)
242 			warnx("getfile failed for %s", getfile->client_name);
243 		if (dolog)
244 			syslog(LOG_NOTICE,
245 			    "getfile failed for %s", getfile->client_name);
246 		return (NULL);
247 	}
248 
249 	if (debug)
250 		warnx("returning server:%s path:%s address: %d.%d.%d.%d",
251 		    res.server_name, res.server_path,
252 		    255 & res.server_address.bp_address_u.ip_addr.net,
253 		    255 & res.server_address.bp_address_u.ip_addr.host,
254 		    255 & res.server_address.bp_address_u.ip_addr.lh,
255 		    255 & res.server_address.bp_address_u.ip_addr.impno);
256 	if (dolog)
257 		syslog(LOG_NOTICE,
258 		    "returning server:%s path:%s address: %d.%d.%d.%d",
259 		    res.server_name, res.server_path,
260 		    255 & res.server_address.bp_address_u.ip_addr.net,
261 		    255 & res.server_address.bp_address_u.ip_addr.host,
262 		    255 & res.server_address.bp_address_u.ip_addr.lh,
263 		    255 & res.server_address.bp_address_u.ip_addr.impno);
264 	return (&res);
265 }
266 
267 int
268 lookup_bootparam(char *client, char *client_canonical, char *id,
269     char **server, char **path)
270 {
271 	FILE   *f;
272 	static char buf[BUFSIZ];
273 	char   *bp, *word = NULL;
274 	size_t  idlen = id == NULL ? 0 : strlen(id);
275 	int	contin = 0, found = 0;
276 
277 	f = fopen(bootpfile, "r");
278 	if (f == NULL)
279 		return EINVAL;	/* ? */
280 
281 	while (fgets(buf, sizeof buf, f)) {
282 		int	wascontin = contin;
283 
284 		contin = buf[strlen(buf) - 2] == '\\';
285 		bp = buf + strspn(buf, " \t\n");
286 
287 		switch (wascontin) {
288 		case -1:
289 			/* Continuation of uninteresting line */
290 			contin *= -1;
291 			continue;
292 		case 0:
293 			/* New line */
294 			contin *= -1;
295 			if (*bp == '#')
296 				continue;
297 			if ((word = strsep(&bp, " \t\n")) == NULL)
298 				continue;
299 			/* See if this line's client is the one we are
300 			 * looking for */
301 			if (strcasecmp(word, client) != 0) {
302 				/*
303 				 * If it didn't match, try getting the
304 				 * canonical host name of the client
305 				 * on this line and comparing that to
306 				 * the client we are looking for
307 				 */
308 				struct hostent *hp = gethostbyname(word);
309 				if (hp == NULL || strcasecmp(hp->h_name, client))
310 					continue;
311 			}
312 			contin *= -1;
313 			break;
314 		case 1:
315 			/* Continued line we want to parse below */
316 			break;
317 		}
318 
319 		if (client_canonical)
320 			strlcpy(client_canonical, word, MAX_MACHINE_NAME);
321 
322 		/* We have found a line for CLIENT */
323 		if (id == NULL) {
324 			(void) fclose(f);
325 			return 0;
326 		}
327 
328 		/* Look for a value for the parameter named by ID */
329 		while ((word = strsep(&bp, " \t\n")) != NULL) {
330 			if (!strncmp(word, id, idlen) && word[idlen] == '=') {
331 				/* We have found the entry we want */
332 				*server = &word[idlen + 1];
333 				*path = strchr(*server, ':');
334 				if (*path == NULL)
335 					/* Malformed entry */
336 					continue;
337 				*(*path)++ = '\0';
338 				(void) fclose(f);
339 				return 0;
340 			}
341 		}
342 
343 		found = 1;
344 	}
345 
346 	(void) fclose(f);
347 	return found ? ENOENT : EPERM;
348 }
349