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