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 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 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 * 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 * 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 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