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