16751Swpaul/*
26751Swpaul
38857SrgrimesThis code is not copyright, and is placed in the public domain. Feel free to
46751Swpauluse and modify. Please send modifications and/or suggestions + bug fixes to
56751Swpaul
66751Swpaul        Klas Heggemann <klas@nada.kth.se>
76751Swpaul
86751Swpaul*/
96751Swpaul
1029102Scharnier#ifndef lint
1129102Scharnierstatic const char rcsid[] =
1250479Speter  "$FreeBSD: stable/10/usr.sbin/bootparamd/bootparamd/bootparamd.c 320780 2017-07-07 15:09:08Z asomers $";
1329102Scharnier#endif /* not lint */
146751Swpaul
15137675Sbz#ifdef YP
166751Swpaul#include <rpc/rpc.h>
179718Swpaul#include <rpcsvc/yp_prot.h>
189718Swpaul#include <rpcsvc/ypclnt.h>
19137675Sbz#endif
206751Swpaul#include "bootparam_prot.h"
2129102Scharnier#include <ctype.h>
2229102Scharnier#include <err.h>
2329102Scharnier#include <netdb.h>
246751Swpaul#include <stdio.h>
2522954Smpp#include <string.h>
2629102Scharnier#include <syslog.h>
2729102Scharnier#include <unistd.h>
286751Swpaul#include <sys/types.h>
296751Swpaul#include <sys/socket.h>
306751Swpaulextern int debug, dolog;
31175823Srinkextern in_addr_t route_addr;
326751Swpaulextern char *bootpfile;
336751Swpaul
346751Swpaul#define MAXLEN 800
356751Swpaul
366751Swpaulstruct hostent *he;
376751Swpaulstatic char buffer[MAXLEN];
386751Swpaulstatic char hostname[MAX_MACHINE_NAME];
396751Swpaulstatic char askname[MAX_MACHINE_NAME];
406751Swpaulstatic char path[MAX_PATH_LEN];
416751Swpaulstatic char domain_name[MAX_MACHINE_NAME];
426751Swpaul
43173412Skevloint getthefile(char *, char *, char *, int);
44173412Skevloint checkhost(char *, char *, int);
458857Srgrimes
466751Swpaulbp_whoami_res *
47121554Speterbootparamproc_whoami_1_svc(whoami, req)
486751Swpaulbp_whoami_arg *whoami;
49121554Speterstruct svc_req *req;
506751Swpaul{
51175823Srink  in_addr_t haddr;
526751Swpaul  static bp_whoami_res res;
538857Srgrimes  if (debug)
548857Srgrimes    fprintf(stderr,"whoami got question for %d.%d.%d.%d\n",
556751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.net,
566751Swpaul	    255 & whoami->client_address.bp_address_u.ip_addr.host,
576751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.lh,
586751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.impno);
598857Srgrimes  if (dolog)
608857Srgrimes    syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d\n",
616751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.net,
626751Swpaul	    255 & whoami->client_address.bp_address_u.ip_addr.host,
636751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.lh,
646751Swpaul	    255 &  whoami->client_address.bp_address_u.ip_addr.impno);
656751Swpaul
668857Srgrimes  bcopy((char *)&whoami->client_address.bp_address_u.ip_addr, (char *)&haddr,
676751Swpaul	sizeof(haddr));
686751Swpaul  he = gethostbyaddr((char *)&haddr,sizeof(haddr),AF_INET);
696751Swpaul  if ( ! he ) goto failed;
706751Swpaul
7129102Scharnier  if (debug) warnx("this is host %s", he->h_name);
726751Swpaul  if (dolog) syslog(LOG_NOTICE,"This is host %s\n", he->h_name);
738857Srgrimes
7430570Sjoerg  strncpy(askname, he->h_name, sizeof(askname));
7530570Sjoerg  askname[sizeof(askname)-1] = 0;
7630570Sjoerg
7730570Sjoerg  if (checkhost(askname, hostname, sizeof hostname) ) {
786751Swpaul    res.client_name = hostname;
796751Swpaul    getdomainname(domain_name, MAX_MACHINE_NAME);
806751Swpaul    res.domain_name = domain_name;
816751Swpaul
826751Swpaul    if (  res.router_address.address_type != IP_ADDR_TYPE ) {
836751Swpaul      res.router_address.address_type = IP_ADDR_TYPE;
84175823Srink      bcopy( &route_addr, &res.router_address.bp_address_u.ip_addr, sizeof(in_addr_t));
856751Swpaul    }
866751Swpaul    if (debug) fprintf(stderr,
878857Srgrimes		       "Returning %s   %s    %d.%d.%d.%d\n",
886751Swpaul		       res.client_name,
896751Swpaul		       res.domain_name,
906751Swpaul		       255 &  res.router_address.bp_address_u.ip_addr.net,
916751Swpaul		       255 & res.router_address.bp_address_u.ip_addr.host,
926751Swpaul		       255 &  res.router_address.bp_address_u.ip_addr.lh,
936751Swpaul		       255 & res.router_address.bp_address_u.ip_addr.impno);
946751Swpaul    if (dolog) syslog(LOG_NOTICE,
958857Srgrimes		       "Returning %s   %s    %d.%d.%d.%d\n",
966751Swpaul		       res.client_name,
976751Swpaul		       res.domain_name,
986751Swpaul		       255 &  res.router_address.bp_address_u.ip_addr.net,
996751Swpaul		       255 & res.router_address.bp_address_u.ip_addr.host,
1006751Swpaul		       255 &  res.router_address.bp_address_u.ip_addr.lh,
1016751Swpaul		       255 & res.router_address.bp_address_u.ip_addr.impno);
1026751Swpaul
1036751Swpaul    return(&res);
1046751Swpaul  }
1056751Swpaul failed:
10629102Scharnier  if (debug) warnx("whoami failed");
1076751Swpaul  if (dolog) syslog(LOG_NOTICE,"whoami failed\n");
1086751Swpaul  return(NULL);
1096751Swpaul}
1106751Swpaul
1116751Swpaul
1126751Swpaulbp_getfile_res *
113121554Speter  bootparamproc_getfile_1_svc(getfile, req)
1146751Swpaulbp_getfile_arg *getfile;
115121554Speterstruct svc_req *req;
1166751Swpaul{
117229403Sed  char *where;
1186751Swpaul  static bp_getfile_res res;
1196751Swpaul
1208857Srgrimes  if (debug)
12129102Scharnier    warnx("getfile got question for \"%s\" and file \"%s\"",
1226751Swpaul	    getfile->client_name, getfile->file_id);
1236751Swpaul
1248857Srgrimes  if (dolog)
1256751Swpaul    syslog(LOG_NOTICE,"getfile got question for \"%s\" and file \"%s\"\n",
1266751Swpaul	    getfile->client_name, getfile->file_id);
1276751Swpaul
1286751Swpaul  he = NULL;
1296751Swpaul  he = gethostbyname(getfile->client_name);
1306751Swpaul  if (! he ) goto failed;
1316751Swpaul
13230570Sjoerg  strncpy(askname, he->h_name, sizeof(askname));
13330570Sjoerg  askname[sizeof(askname)-1] = 0;
13430570Sjoerg
13536794Simp  if (getthefile(askname, getfile->file_id,buffer,sizeof(buffer))) {
136229403Sed    if ( (where = strchr(buffer,':')) ) {
1376751Swpaul      /* buffer is re-written to contain the name of the info of file */
1386751Swpaul      strncpy(hostname, buffer, where - buffer);
1396751Swpaul      hostname[where - buffer] = '\0';
1406751Swpaul      where++;
1416751Swpaul      strcpy(path, where);
1426751Swpaul      he = gethostbyname(hostname);
1436751Swpaul      if ( !he ) goto failed;
1446751Swpaul      bcopy( he->h_addr, &res.server_address.bp_address_u.ip_addr, 4);
1456751Swpaul      res.server_name = hostname;
1466751Swpaul      res.server_path = path;
1476751Swpaul      res.server_address.address_type = IP_ADDR_TYPE;
1486751Swpaul    }
1496751Swpaul    else { /* special for dump, answer with null strings */
1506751Swpaul      if (!strcmp(getfile->file_id, "dump")) {
1516751Swpaul	res.server_name = "";
1526751Swpaul	res.server_path = "";
1536751Swpaul        res.server_address.address_type = IP_ADDR_TYPE;
1546751Swpaul	bzero(&res.server_address.bp_address_u.ip_addr,4);
1556751Swpaul      } else goto failed;
1566751Swpaul    }
1578857Srgrimes    if (debug)
1586751Swpaul      fprintf(stderr, "returning server:%s path:%s address: %d.%d.%d.%d\n",
1596751Swpaul	     res.server_name, res.server_path,
1606751Swpaul	     255 &  res.server_address.bp_address_u.ip_addr.net,
1616751Swpaul	     255 & res.server_address.bp_address_u.ip_addr.host,
1626751Swpaul	     255 &  res.server_address.bp_address_u.ip_addr.lh,
1636751Swpaul	     255 & res.server_address.bp_address_u.ip_addr.impno);
1648857Srgrimes    if (dolog)
1656751Swpaul      syslog(LOG_NOTICE, "returning server:%s path:%s address: %d.%d.%d.%d\n",
1666751Swpaul	     res.server_name, res.server_path,
1676751Swpaul	     255 &  res.server_address.bp_address_u.ip_addr.net,
1686751Swpaul	     255 & res.server_address.bp_address_u.ip_addr.host,
1696751Swpaul	     255 &  res.server_address.bp_address_u.ip_addr.lh,
1706751Swpaul	     255 & res.server_address.bp_address_u.ip_addr.impno);
1716751Swpaul    return(&res);
1726751Swpaul  }
1736751Swpaul  failed:
17429102Scharnier  if (debug) warnx("getfile failed for %s", getfile->client_name);
1758857Srgrimes  if (dolog) syslog(LOG_NOTICE,
1766751Swpaul		    "getfile failed for %s\n", getfile->client_name);
1776751Swpaul  return(NULL);
1786751Swpaul}
1796751Swpaul
1806751Swpaul/*    getthefile return 1 and fills the buffer with the information
1816751Swpaul      of the file, e g "host:/export/root/client" if it can be found.
1828857Srgrimes      If the host is in the database, but the file is not, the buffer
1836751Swpaul      will be empty. (This makes it possible to give the special
1846751Swpaul      empty answer for the file "dump")   */
1856751Swpaul
18629102Scharnierint
18736794Simpgetthefile(askname,fileid,buffer,blen)
1886751Swpaulchar *askname;
1896751Swpaulchar *fileid, *buffer;
19036794Simpint blen;
1916751Swpaul{
1926751Swpaul  FILE *bpf;
1936751Swpaul  char  *where;
194201521Sed#ifdef YP
1956751Swpaul  static char *result;
1966751Swpaul  int resultlen;
1976751Swpaul  static char *yp_domain;
198137675Sbz#endif
1996751Swpaul
2006751Swpaul  int ch, pch, fid_len, res = 0;
2016751Swpaul  int match = 0;
202320780Sasomers#define INFOLEN 1343
203320780Sasomers  _Static_assert(INFOLEN >= MAX_FILEID + MAX_PATH_LEN+MAX_MACHINE_NAME + 3,
204320780Sasomers		  "INFOLEN isn't large enough");
205320780Sasomers  char info[INFOLEN + 1];
2066751Swpaul
2076751Swpaul  bpf = fopen(bootpfile, "r");
20829102Scharnier  if ( ! bpf )
20929102Scharnier    errx(1, "no %s", bootpfile);
2106751Swpaul
21130570Sjoerg  /* XXX see comment below */
21230570Sjoerg  while ( fscanf(bpf, "%255s", hostname) > 0  && !match ) {
2136751Swpaul    if ( *hostname != '#' ) { /* comment */
2146751Swpaul      if ( ! strcmp(hostname, askname) ) {
2156751Swpaul	match = 1;
2166751Swpaul      } else {
2176751Swpaul	he = gethostbyname(hostname);
2186751Swpaul	if (he && !strcmp(he->h_name, askname)) match = 1;
2196751Swpaul      }
2206751Swpaul    }
2216751Swpaul    if (*hostname == '+' ) { /* NIS */
222137675Sbz#ifdef YP
2236751Swpaul      if (yp_get_default_domain(&yp_domain)) {
22429102Scharnier	 if (debug) warn("NIS");
2256751Swpaul	 return(0);
2266751Swpaul      }
2276751Swpaul      if (yp_match(yp_domain, "bootparams", askname, strlen(askname),
2286751Swpaul		&result, &resultlen))
2296751Swpaul	return (0);
2306751Swpaul      if (strstr(result, fileid) == NULL) {
2316751Swpaul	buffer[0] = '\0';
2326751Swpaul      } else {
23336794Simp	snprintf(buffer, blen,
23436794Simp		"%s",strchr(strstr(result,fileid), '=') + 1);
2356751Swpaul	if (strchr(buffer, ' ') != NULL)
2366751Swpaul	  *(char *)(strchr(buffer, ' ')) = '\0';
2376751Swpaul      }
2386751Swpaul      if (fclose(bpf))
23929102Scharnier        warnx("could not close %s", bootpfile);
2406751Swpaul      return(1);
241137675Sbz#else
242137675Sbz      return(0);	/* ENOTSUP */
243137675Sbz#endif
2446751Swpaul    }
2456751Swpaul    /* skip to next entry */
2466751Swpaul    if ( match ) break;
2478857Srgrimes    pch = ch = getc(bpf);
2486751Swpaul    while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
2496751Swpaul      pch = ch; ch = getc(bpf);
2506751Swpaul    }
2516751Swpaul  }
2526751Swpaul
2536751Swpaul  /* if match is true we read the rest of the line to get the
2546751Swpaul     info of the file */
2556751Swpaul
2566751Swpaul  if (match) {
2576751Swpaul    fid_len = strlen(fileid);
258320780Sasomers#define AS_FORMAT(d)	"%" #d "s"
259320780Sasomers#define REXPAND(d) AS_FORMAT(d)	/* Force another preprocessor expansion */
260320780Sasomers    while ( ! res && (fscanf(bpf, REXPAND(INFOLEN), info)) > 0) {
2616751Swpaul      ch = getc(bpf);                                /* and a character */
2626751Swpaul      if ( *info != '#' ) {                          /* Comment ? */
2636751Swpaul	if (! strncmp(info, fileid, fid_len) && *(info + fid_len) == '=') {
2646751Swpaul	  where = info + fid_len + 1;
2656751Swpaul	  if ( isprint( *where )) {
2666751Swpaul	    strcpy(buffer, where);                   /* found file */
2676751Swpaul	    res = 1; break;
2686751Swpaul	  }
2698857Srgrimes	} else {
2708857Srgrimes	  while (isspace(ch) && ch != '\n') ch = getc(bpf);
2716751Swpaul	                                             /* read to end of line */
2726751Swpaul	  if ( ch == '\n' ) {                        /* didn't find it */
2736751Swpaul	    res = -1; break;                         /* but host is there */
2746751Swpaul	  }
2756751Swpaul	  if ( ch == '\\' ) {                        /* more info */
2766751Swpaul	    ch = getc(bpf);                          /* maybe on next line */
2776751Swpaul	    if (ch == '\n') continue;                /* read it in next loop */
2786751Swpaul	    ungetc(ch, bpf); ungetc('\\',bpf); /* push the character(s) back */
2796751Swpaul	  } else ungetc(ch, bpf);              /* but who know what a `\` is */
2806751Swpaul	}                                      /* needed for. */
2816751Swpaul      } else break;                            /* a commented rest-of-line */
2826751Swpaul    }
2836751Swpaul  }
28429102Scharnier  if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
2856751Swpaul  if ( res == -1) buffer[0] = '\0';            /* host found, file not */
2866751Swpaul  return(match);
2876751Swpaul}
2886751Swpaul
2896751Swpaul/* checkhost puts the hostname found in the database file in
2906751Swpaul   the hostname-variable and returns 1, if askname is a valid
2916751Swpaul   name for a host in the database */
2926751Swpaul
29329102Scharnierint
29430570Sjoergcheckhost(askname, hostname, len)
2956751Swpaulchar *askname;
2966751Swpaulchar *hostname;
29730570Sjoergint len;
2986751Swpaul{
2996751Swpaul  int ch, pch;
3006751Swpaul  FILE *bpf;
3016751Swpaul  int res = 0;
302201521Sed#ifdef YP
3036751Swpaul  static char *result;
3046751Swpaul  int resultlen;
3056751Swpaul  static char *yp_domain;
306137675Sbz#endif
3076751Swpaul
3086751Swpaul/*  struct hostent *cmp_he;*/
3096751Swpaul
3106751Swpaul  bpf = fopen(bootpfile, "r");
31129102Scharnier  if ( ! bpf )
31229102Scharnier    errx(1, "no %s", bootpfile);
3136751Swpaul
31430570Sjoerg  /* XXX there is no way in ISO C to specify the maximal length for a
31530570Sjoerg     conversion in a variable way */
31630570Sjoerg  while ( fscanf(bpf, "%254s", hostname) > 0 ) {
3176751Swpaul    if ( *hostname != '#' ) { /* comment */
3186751Swpaul      if ( ! strcmp(hostname, askname) ) {
3196751Swpaul        /* return true for match of hostname */
3206751Swpaul        res = 1;
3216751Swpaul        break;
3226751Swpaul      } else {
3236751Swpaul        /* check the alias list */
3246751Swpaul        he = NULL;
3256751Swpaul        he = gethostbyname(hostname);
3266751Swpaul        if (he && !strcmp(askname, he->h_name)) {
3276751Swpaul  	  res = 1;
3286751Swpaul	  break;
3296751Swpaul        }
3306751Swpaul      }
3316751Swpaul    }
3326751Swpaul    if (*hostname == '+' ) { /* NIS */
333137675Sbz#ifdef YP
3346751Swpaul      if (yp_get_default_domain(&yp_domain)) {
33529102Scharnier	 if (debug) warn("NIS");
3366751Swpaul	 return(0);
3376751Swpaul      }
3386751Swpaul      if (!yp_match(yp_domain, "bootparams", askname, strlen(askname),
3396751Swpaul		&result, &resultlen)) {
3406751Swpaul        /* return true for match of hostname */
3416751Swpaul        he = NULL;
3426751Swpaul        he = gethostbyname(askname);
3436751Swpaul        if (he && !strcmp(askname, he->h_name)) {
3446751Swpaul  	  res = 1;
34530570Sjoerg	  snprintf(hostname, len, "%s", he->h_name);
3466751Swpaul	}
3476751Swpaul      }
3486751Swpaul      if (fclose(bpf))
34929102Scharnier        warnx("could not close %s", bootpfile);
3506751Swpaul      return(res);
351137675Sbz#else
352137675Sbz      return(0);	/* ENOTSUP */
353137675Sbz#endif
3546751Swpaul    }
3556751Swpaul    /* skip to next entry */
3568857Srgrimes    pch = ch = getc(bpf);
3576751Swpaul    while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
3586751Swpaul      pch = ch; ch = getc(bpf);
3596751Swpaul    }
3606751Swpaul  }
36129102Scharnier  if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
3626751Swpaul  return(res);
3636751Swpaul}
364