1108930Speter/* $FreeBSD$ */ 244743Smarkm /* 344743Smarkm * Routines to parse an inetd.conf or tlid.conf file. This would be a great 444743Smarkm * job for a PERL script. 544743Smarkm * 644743Smarkm * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 744743Smarkm */ 844743Smarkm 944743Smarkm#ifndef lint 1044743Smarkmstatic char sccsid[] = "@(#) inetcf.c 1.7 97/02/12 02:13:23"; 1144743Smarkm#endif 1244743Smarkm 1344743Smarkm#include <sys/types.h> 1444743Smarkm#include <sys/stat.h> 1544743Smarkm#include <stdio.h> 1644743Smarkm#include <errno.h> 1744743Smarkm#include <string.h> 1844743Smarkm 1944743Smarkmextern int errno; 2044743Smarkmextern void exit(); 21108930Speterextern char *malloc(); 2244743Smarkm 2344743Smarkm#include "tcpd.h" 2444743Smarkm#include "inetcf.h" 2544743Smarkm 2644743Smarkm /* 2744743Smarkm * Network configuration files may live in unusual places. Here are some 2844743Smarkm * guesses. Shorter names follow longer ones. 2944743Smarkm */ 3044743Smarkmchar *inet_files[] = { 3144743Smarkm "/private/etc/inetd.conf", /* NEXT */ 3244743Smarkm "/etc/inet/inetd.conf", /* SYSV4 */ 3344743Smarkm "/usr/etc/inetd.conf", /* IRIX?? */ 3444743Smarkm "/etc/inetd.conf", /* BSD */ 3544743Smarkm "/etc/net/tlid.conf", /* SYSV4?? */ 3644743Smarkm "/etc/saf/tlid.conf", /* SYSV4?? */ 3744743Smarkm "/etc/tlid.conf", /* SYSV4?? */ 3844743Smarkm 0, 3944743Smarkm}; 4044743Smarkm 4144743Smarkmstatic void inet_chk(); 4244743Smarkmstatic char *base_name(); 4344743Smarkm 4444743Smarkm /* 4544743Smarkm * Structure with everything we know about a service. 4644743Smarkm */ 4744743Smarkmstruct inet_ent { 4844743Smarkm struct inet_ent *next; 4944743Smarkm int type; 5044743Smarkm char name[1]; 5144743Smarkm}; 5244743Smarkm 5344743Smarkmstatic struct inet_ent *inet_list = 0; 5444743Smarkm 5544743Smarkmstatic char whitespace[] = " \t\r\n"; 5644743Smarkm 5744743Smarkm/* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */ 5844743Smarkm 5944743Smarkmchar *inet_cfg(conf) 6044743Smarkmchar *conf; 6144743Smarkm{ 6244743Smarkm char buf[BUFSIZ]; 6344743Smarkm FILE *fp; 6444743Smarkm char *service; 6544743Smarkm char *protocol; 6644743Smarkm char *user; 6744743Smarkm char *path; 6844743Smarkm char *arg0; 6944743Smarkm char *arg1; 7044743Smarkm struct tcpd_context saved_context; 7144743Smarkm char *percent_m(); 7244743Smarkm int i; 7344743Smarkm struct stat st; 7444743Smarkm 7544743Smarkm saved_context = tcpd_context; 7644743Smarkm 7744743Smarkm /* 7844743Smarkm * The inetd.conf (or tlid.conf) information is so useful that we insist 7944743Smarkm * on its availability. When no file is given run a series of educated 8044743Smarkm * guesses. 8144743Smarkm */ 8244743Smarkm if (conf != 0) { 8344743Smarkm if ((fp = fopen(conf, "r")) == 0) { 8444743Smarkm fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf); 8544743Smarkm exit(1); 8644743Smarkm } 8744743Smarkm } else { 8844743Smarkm for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++) 8944743Smarkm /* void */ ; 9044743Smarkm if (fp == 0) { 9144743Smarkm fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n"); 9244743Smarkm fprintf(stderr, "Please specify its location.\n"); 9344743Smarkm exit(1); 9444743Smarkm } 9544743Smarkm conf = inet_files[i]; 9644743Smarkm check_path(conf, &st); 9744743Smarkm } 9844743Smarkm 9944743Smarkm /* 10044743Smarkm * Process the file. After the 7.0 wrapper release it became clear that 10144743Smarkm * there are many more inetd.conf formats than the 8 systems that I had 10244743Smarkm * studied. EP/IX uses a two-line specification for rpc services; HP-UX 10344743Smarkm * permits long lines to be broken with backslash-newline. 10444743Smarkm */ 10544743Smarkm tcpd_context.file = conf; 10644743Smarkm tcpd_context.line = 0; 10744743Smarkm while (xgets(buf, sizeof(buf), fp)) { 10844743Smarkm service = strtok(buf, whitespace); /* service */ 10944743Smarkm if (service == 0 || *service == '#') 11044743Smarkm continue; 11144743Smarkm if (STR_NE(service, "stream") && STR_NE(service, "dgram")) 11244743Smarkm strtok((char *) 0, whitespace); /* endpoint */ 11344743Smarkm protocol = strtok((char *) 0, whitespace); 11444743Smarkm (void) strtok((char *) 0, whitespace); /* wait */ 11544743Smarkm if ((user = strtok((char *) 0, whitespace)) == 0) 11644743Smarkm continue; 11744743Smarkm if (user[0] == '/') { /* user */ 11844743Smarkm path = user; 11944743Smarkm } else { /* path */ 12044743Smarkm if ((path = strtok((char *) 0, whitespace)) == 0) 12144743Smarkm continue; 12244743Smarkm } 12344743Smarkm if (path[0] == '?') /* IRIX optional service */ 12444743Smarkm path++; 12544743Smarkm if (STR_EQ(path, "internal")) 12644743Smarkm continue; 12744743Smarkm if (path[strspn(path, "-0123456789")] == 0) { 12844743Smarkm 12944743Smarkm /* 13044743Smarkm * ConvexOS puts RPC version numbers before path names. Jukka 13144743Smarkm * Ukkonen <ukkonen@csc.fi>. 13244743Smarkm */ 13344743Smarkm if ((path = strtok((char *) 0, whitespace)) == 0) 13444743Smarkm continue; 13544743Smarkm } 13644743Smarkm if ((arg0 = strtok((char *) 0, whitespace)) == 0) { 13744743Smarkm tcpd_warn("incomplete line"); 13844743Smarkm continue; 13944743Smarkm } 14044743Smarkm if (arg0[strspn(arg0, "0123456789")] == 0) { 14144743Smarkm 14244743Smarkm /* 14344743Smarkm * We're reading a tlid.conf file, the format is: 14444743Smarkm * 14544743Smarkm * ...stuff... path arg_count arguments mod_count modules 14644743Smarkm */ 14744743Smarkm if ((arg0 = strtok((char *) 0, whitespace)) == 0) { 14844743Smarkm tcpd_warn("incomplete line"); 14944743Smarkm continue; 15044743Smarkm } 15144743Smarkm } 15244743Smarkm if ((arg1 = strtok((char *) 0, whitespace)) == 0) 15344743Smarkm arg1 = ""; 15444743Smarkm 15544743Smarkm inet_chk(protocol, path, arg0, arg1); 15644743Smarkm } 15744743Smarkm fclose(fp); 15844743Smarkm tcpd_context = saved_context; 15944743Smarkm return (conf); 16044743Smarkm} 16144743Smarkm 16244743Smarkm/* inet_chk - examine one inetd.conf (tlid.conf?) entry */ 16344743Smarkm 16444743Smarkmstatic void inet_chk(protocol, path, arg0, arg1) 16544743Smarkmchar *protocol; 16644743Smarkmchar *path; 16744743Smarkmchar *arg0; 16844743Smarkmchar *arg1; 16944743Smarkm{ 17044743Smarkm char daemon[BUFSIZ]; 17144743Smarkm struct stat st; 17244743Smarkm int wrap_status = WR_MAYBE; 17344743Smarkm char *base_name_path = base_name(path); 17444743Smarkm char *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0); 17544743Smarkm 17644743Smarkm /* 17744743Smarkm * Always warn when the executable does not exist or when it is not 17844743Smarkm * executable. 17944743Smarkm */ 18044743Smarkm if (check_path(path, &st) < 0) { 18144743Smarkm tcpd_warn("%s: not found: %m", path); 18244743Smarkm } else if ((st.st_mode & 0100) == 0) { 18344743Smarkm tcpd_warn("%s: not executable", path); 18444743Smarkm } 18544743Smarkm 18644743Smarkm /* 18744743Smarkm * Cheat on the miscd tests, nobody uses it anymore. 18844743Smarkm */ 18944743Smarkm if (STR_EQ(base_name_path, "miscd")) { 19044743Smarkm inet_set(arg0, WR_YES); 19144743Smarkm return; 19244743Smarkm } 19344743Smarkm 19444743Smarkm /* 19544743Smarkm * While we are here... 19644743Smarkm */ 19744743Smarkm if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd")) 19844743Smarkm tcpd_warn("%s may be an insecure service", tcpd_proc_name); 19944743Smarkm 20044743Smarkm /* 20144743Smarkm * The tcpd program gets most of the attention. 20244743Smarkm */ 20344743Smarkm if (STR_EQ(base_name_path, "tcpd")) { 20444743Smarkm 20544743Smarkm if (STR_EQ(tcpd_proc_name, "tcpd")) 20644743Smarkm tcpd_warn("%s is recursively calling itself", tcpd_proc_name); 20744743Smarkm 20844743Smarkm wrap_status = WR_YES; 20944743Smarkm 21044743Smarkm /* 21144743Smarkm * Check: some sites install the wrapper set-uid. 21244743Smarkm */ 21344743Smarkm if ((st.st_mode & 06000) != 0) 21444743Smarkm tcpd_warn("%s: file is set-uid or set-gid", path); 21544743Smarkm 21644743Smarkm /* 21744743Smarkm * Check: some sites insert tcpd in inetd.conf, instead of replacing 21844743Smarkm * the daemon pathname. 21944743Smarkm */ 22044743Smarkm if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1))) 22144743Smarkm tcpd_warn("%s inserted before %s", path, arg0); 22244743Smarkm 22344743Smarkm /* 22444743Smarkm * Check: make sure files exist and are executable. On some systems 22544743Smarkm * the network daemons are set-uid so we cannot complain. Note that 22644743Smarkm * tcpd takes the basename only in case of absolute pathnames. 22744743Smarkm */ 22844743Smarkm if (arg0[0] == '/') { /* absolute path */ 22944743Smarkm if (check_path(arg0, &st) < 0) { 23044743Smarkm tcpd_warn("%s: not found: %m", arg0); 23144743Smarkm } else if ((st.st_mode & 0100) == 0) { 23244743Smarkm tcpd_warn("%s: not executable", arg0); 23344743Smarkm } 23444743Smarkm } else { /* look in REAL_DAEMON_DIR */ 23544743Smarkm sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); 23644743Smarkm if (check_path(daemon, &st) < 0) { 23744743Smarkm tcpd_warn("%s: not found in %s: %m", 23844743Smarkm arg0, REAL_DAEMON_DIR); 23944743Smarkm } else if ((st.st_mode & 0100) == 0) { 24044743Smarkm tcpd_warn("%s: not executable", daemon); 24144743Smarkm } 24244743Smarkm } 24344743Smarkm 24444743Smarkm } else { 24544743Smarkm 24644743Smarkm /* 24744743Smarkm * No tcpd program found. Perhaps they used the "simple installation" 24844743Smarkm * recipe. Look for a file with the same basename in REAL_DAEMON_DIR. 24944743Smarkm * Draw some conservative conclusions when a distinct file is found. 25044743Smarkm */ 25144743Smarkm sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); 25244743Smarkm if (STR_EQ(path, daemon)) { 25345256Sache#ifdef __FreeBSD__ 25445256Sache wrap_status = WR_MAYBE; 25545256Sache#else 25644743Smarkm wrap_status = WR_NOT; 25745256Sache#endif 25844743Smarkm } else if (check_path(daemon, &st) >= 0) { 25944743Smarkm wrap_status = WR_MAYBE; 26044743Smarkm } else if (errno == ENOENT) { 26144743Smarkm wrap_status = WR_NOT; 26244743Smarkm } else { 26344743Smarkm tcpd_warn("%s: file lookup: %m", daemon); 26444743Smarkm wrap_status = WR_MAYBE; 26544743Smarkm } 26644743Smarkm } 26744743Smarkm 26844743Smarkm /* 26944743Smarkm * Alas, we cannot wrap rpc/tcp services. 27044743Smarkm */ 27144743Smarkm if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp")) 27244743Smarkm tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name); 27344743Smarkm 27444743Smarkm inet_set(tcpd_proc_name, wrap_status); 27544743Smarkm} 27644743Smarkm 27744743Smarkm/* inet_set - remember service status */ 27844743Smarkm 27944743Smarkmvoid inet_set(name, type) 28044743Smarkmchar *name; 28144743Smarkmint type; 28244743Smarkm{ 28344743Smarkm struct inet_ent *ip = 28444743Smarkm (struct inet_ent *) malloc(sizeof(struct inet_ent) + strlen(name)); 28544743Smarkm 28644743Smarkm if (ip == 0) { 28744743Smarkm fprintf(stderr, "out of memory\n"); 28844743Smarkm exit(1); 28944743Smarkm } 29044743Smarkm ip->next = inet_list; 29144743Smarkm strcpy(ip->name, name); 29244743Smarkm ip->type = type; 29344743Smarkm inet_list = ip; 29444743Smarkm} 29544743Smarkm 29644743Smarkm/* inet_get - look up service status */ 29744743Smarkm 29844743Smarkmint inet_get(name) 29944743Smarkmchar *name; 30044743Smarkm{ 30144743Smarkm struct inet_ent *ip; 30244743Smarkm 30344743Smarkm if (inet_list == 0) 30444743Smarkm return (WR_MAYBE); 30544743Smarkm 30644743Smarkm for (ip = inet_list; ip; ip = ip->next) 30744743Smarkm if (STR_EQ(ip->name, name)) 30844743Smarkm return (ip->type); 30944743Smarkm 31044743Smarkm return (-1); 31144743Smarkm} 31244743Smarkm 31344743Smarkm/* base_name - compute last pathname component */ 31444743Smarkm 31544743Smarkmstatic char *base_name(path) 31644743Smarkmchar *path; 31744743Smarkm{ 31844743Smarkm char *cp; 31944743Smarkm 32044743Smarkm if ((cp = strrchr(path, '/')) != 0) 32144743Smarkm path = cp + 1; 32244743Smarkm return (path); 32344743Smarkm} 324