1 /* $OpenBSD: bootparamd.c,v 1.18 2007/02/18 20:33:45 jmc 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 * Parser rewritten (adding YP support) by Roland McGrath <roland@frob.com> 10 */ 11 12 #include <sys/types.h> 13 #include <sys/ioctl.h> 14 #include <sys/stat.h> 15 #include <sys/socket.h> 16 17 #include <rpc/rpc.h> 18 #include <rpcsvc/bootparam_prot.h> 19 #include <rpcsvc/ypclnt.h> 20 #include <rpcsvc/yp_prot.h> 21 #include <arpa/inet.h> 22 23 #include <stdio.h> 24 #include <netdb.h> 25 #include <ctype.h> 26 #include <syslog.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <err.h> 30 #include <stdlib.h> 31 32 #include "pathnames.h" 33 34 #define MAXLEN 800 35 36 struct hostent *he; 37 static char hostname[MAX_MACHINE_NAME]; 38 static char askname[MAX_MACHINE_NAME]; 39 static char domain_name[MAX_MACHINE_NAME]; 40 41 extern void bootparamprog_1(struct svc_req *, SVCXPRT *); 42 int lookup_bootparam(char *client, char *client_canonical, char *id, 43 char **server, char **path); 44 45 int _rpcsvcdirty = 0; 46 int _rpcpmstart = 0; 47 int debug = 0; 48 int dolog = 0; 49 struct in_addr route_addr; 50 struct sockaddr_in my_addr; 51 extern char *__progname; 52 char *bootpfile = _PATH_BOOTPARAMS; 53 54 extern char *optarg; 55 extern int optind; 56 57 static void 58 usage(void) 59 { 60 extern char *__progname; 61 fprintf(stderr, "usage: %s [-ds] [-f file] [-r router]\n", 62 __progname); 63 exit(1); 64 } 65 66 67 /* 68 * ever familiar 69 */ 70 int 71 main(int argc, char *argv[]) 72 { 73 struct hostent *he; 74 struct stat buf; 75 SVCXPRT *transp; 76 int c; 77 78 while ((c = getopt(argc, argv, "dsr:f:")) != -1) 79 switch (c) { 80 case 'd': 81 debug = 1; 82 break; 83 case 'r': 84 if (inet_aton(optarg, &route_addr) == 1) 85 break; 86 he = gethostbyname(optarg); 87 if (!he) { 88 warnx("no such host: %s", optarg); 89 usage(); 90 } 91 bcopy(he->h_addr, (char *) &route_addr.s_addr, 92 sizeof(route_addr.s_addr)); 93 break; 94 case 'f': 95 bootpfile = optarg; 96 break; 97 case 's': 98 dolog = 1; 99 #ifndef LOG_DAEMON 100 openlog(__progname, 0, 0); 101 #else 102 openlog(__progname, 0, LOG_DAEMON); 103 setlogmask(LOG_UPTO(LOG_NOTICE)); 104 #endif 105 break; 106 default: 107 usage(); 108 } 109 110 if (stat(bootpfile, &buf)) 111 err(1, "%s", bootpfile); 112 113 if (!route_addr.s_addr) { 114 get_myaddress(&my_addr); 115 bcopy(&my_addr.sin_addr.s_addr, &route_addr.s_addr, 116 sizeof(route_addr.s_addr)); 117 } 118 if (!debug) { 119 if (daemon(0, 0)) 120 err(1, "can't detach from terminal"); 121 } 122 123 (void) pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS); 124 125 transp = svcudp_create(RPC_ANYSOCK); 126 if (transp == NULL) 127 errx(1, "can't create udp service"); 128 129 if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1, 130 IPPROTO_UDP)) 131 errx(1, "unable to register BOOTPARAMPROG version %ld, udp", 132 BOOTPARAMVERS); 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((char *) &whoami->client_address.bp_address_u.ip_addr, 158 &haddr, sizeof(haddr)); 159 he = gethostbyaddr((char *) &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 err; 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 err = lookup_bootparam(askname, NULL, getfile->file_id, 226 &res.server_name, &res.server_path); 227 if (err == 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 (err == 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 = fopen(bootpfile, "r"); 271 #ifdef YP 272 static char *ypbuf = NULL; 273 static int ypbuflen = 0; 274 #endif 275 static char buf[BUFSIZ]; 276 char *bp, *word = NULL; 277 size_t idlen = id == NULL ? 0 : strlen(id); 278 int contin = 0, found = 0; 279 280 if (f == NULL) 281 return EINVAL; /* ? */ 282 283 while (fgets(buf, sizeof buf, f)) { 284 int wascontin = contin; 285 286 contin = buf[strlen(buf) - 2] == '\\'; 287 bp = buf + strspn(buf, " \t\n"); 288 289 switch (wascontin) { 290 case -1: 291 /* Continuation of uninteresting line */ 292 contin *= -1; 293 continue; 294 case 0: 295 /* New line */ 296 contin *= -1; 297 if (*bp == '#') 298 continue; 299 if ((word = strsep(&bp, " \t\n")) == NULL) 300 continue; 301 #ifdef YP 302 /* A + in the file means try YP now */ 303 if (!strcmp(word, "+")) { 304 char *ypdom; 305 306 if (yp_get_default_domain(&ypdom) || 307 yp_match(ypdom, "bootparams", client, 308 strlen(client), &ypbuf, &ypbuflen)) 309 continue; 310 bp = ypbuf; 311 word = client; 312 contin *= -1; 313 break; 314 } 315 #endif 316 /* See if this line's client is the one we are 317 * looking for */ 318 if (strcasecmp(word, client) != 0) { 319 /* 320 * If it didn't match, try getting the 321 * canonical host name of the client 322 * on this line and comparing that to 323 * the client we are looking for 324 */ 325 struct hostent *hp = gethostbyname(word); 326 if (hp == NULL || strcasecmp(hp->h_name, client)) 327 continue; 328 } 329 contin *= -1; 330 break; 331 case 1: 332 /* Continued line we want to parse below */ 333 break; 334 } 335 336 if (client_canonical) 337 strlcpy(client_canonical, word, MAX_MACHINE_NAME); 338 339 /* We have found a line for CLIENT */ 340 if (id == NULL) { 341 (void) fclose(f); 342 return 0; 343 } 344 345 /* Look for a value for the parameter named by ID */ 346 while ((word = strsep(&bp, " \t\n")) != NULL) { 347 if (!strncmp(word, id, idlen) && word[idlen] == '=') { 348 /* We have found the entry we want */ 349 *server = &word[idlen + 1]; 350 *path = strchr(*server, ':'); 351 if (*path == NULL) 352 /* Malformed entry */ 353 continue; 354 *(*path)++ = '\0'; 355 (void) fclose(f); 356 return 0; 357 } 358 } 359 360 found = 1; 361 } 362 363 (void) fclose(f); 364 return found ? ENOENT : EPERM; 365 } 366