1: /* 2: * Routines to parse an inetd.conf or tlid.conf file. This would be a great 3: * job for a PERL script. 4: * 5: * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 6: */ 7: 8: #ifndef lint 9: static char sccsid[] = "@(#) inetcf.c 1.5 95/01/30 19:51:48"; 10: #endif 11: 12: #include <sys/types.h> 13: #include <sys/stat.h> 14: #include <stdio.h> 15: #include <errno.h> 16: #include <string.h> 17: 18: extern int errno; 19: extern void exit(); 20: 21: #include "tcpd.h" 22: #include "inetcf.h" 23: 24: /* 25: * Network configuration files may live in unusual places. Here are some 26: * guesses. Shorter names follow longer ones. 27: */ 28: char *inet_files[] = { 29: "/private/etc/inetd.conf", /* NEXT */ 30: "/etc/inet/inetd.conf", /* SYSV4 */ 31: "/usr/etc/inetd.conf", /* IRIX?? */ 32: "/etc/inetd.conf", /* BSD */ 33: "/etc/net/tlid.conf", /* SYSV4?? */ 34: "/etc/saf/tlid.conf", /* SYSV4?? */ 35: "/etc/tlid.conf", /* SYSV4?? */ 36: 0, 37: }; 38: 39: static void inet_chk(); 40: static char *base_name(); 41: 42: /* 43: * Structure with everything we know about a service. 44: */ 45: struct inet_ent { 46: struct inet_ent *next; 47: int type; 48: char name[1]; 49: }; 50: 51: static struct inet_ent *inet_list = 0; 52: 53: static char whitespace[] = " \t\r\n"; 54: 55: /* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */ 56: 57: char *inet_cfg(conf) 58: char *conf; 59: { 60: char buf[BUFSIZ]; 61: FILE *fp; 62: char *service; 63: char *protocol; 64: char *user; 65: char *path; 66: char *arg0; 67: char *arg1; 68: struct tcpd_context saved_context; 69: char *percent_m(); 70: int i; 71: struct stat st; 72: 73: saved_context = tcpd_context; 74: 75: /* 76: * The inetd.conf (or tlid.conf) information is so useful that we insist 77: * on its availability. When no file is given run a series of educated 78: * guesses. 79: */ 80: if (conf != 0) { 81: if ((fp = fopen(conf, "r")) == 0) { 82: fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf); 83: exit(1); 84: } 85: } else { 86: for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++) 87: /* void */ ; 88: if (fp == 0) { 89: fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n"); 90: fprintf(stderr, "Please specify its location.\n"); 91: exit(1); 92: } 93: conf = inet_files[i]; 94: check_path(conf, &st); 95: } 96: 97: /* 98: * Process the file. After the 7.0 wrapper release it became clear that 99: * there are many more inetd.conf formats than the 8 systems that I had 100: * studied. EP/IX uses a two-line specification for rpc services; HP-UX 101: * permits long lines to be broken with backslash-newline. 102: */ 103: tcpd_context.file = conf; 104: tcpd_context.line = 0; 105: while (xgets(buf, sizeof(buf), fp)) { 106: service = strtok(buf, whitespace); /* service */ 107: if (service == 0 || *service == '#') 108: continue; 109: if (STR_NE(service, "stream") && STR_NE(service, "dgram")) 110: strtok((char *) 0, whitespace); /* endpoint */ 111: protocol = strtok((char *) 0, whitespace); 112: (void) strtok((char *) 0, whitespace); /* wait */ 113: if ((user = strtok((char *) 0, whitespace)) == 0) 114: continue; 115: if (user[0] == '/') { /* user */ 116: path = user; 117: } else { /* path */ 118: if ((path = strtok((char *) 0, whitespace)) == 0) 119: continue; 120: } 121: if (STR_EQ(path, "internal")) 122: continue; 123: if (path[strspn(path, "-0123456789")] == 0) { 124: 125: /* 126: * ConvexOS puts RPC version numbers before path names. Jukka 127: * Ukkonen <ukkonen@csc.fi>. 128: */ 129: if ((path = strtok((char *) 0, whitespace)) == 0) 130: continue; 131: } 132: if ((arg0 = strtok((char *) 0, whitespace)) == 0) { 133: tcpd_warn("incomplete line"); 134: continue; 135: } 136: if (arg0[strspn(arg0, "0123456789")] == 0) { 137: 138: /* 139: * We're reading a tlid.conf file, the format is: 140: * 141: * ...stuff... path arg_count arguments mod_count modules 142: */ 143: if ((arg0 = strtok((char *) 0, whitespace)) == 0) { 144: tcpd_warn("incomplete line"); 145: continue; 146: } 147: } 148: if ((arg1 = strtok((char *) 0, whitespace)) == 0) 149: arg1 = ""; 150: 151: inet_chk(protocol, path, arg0, arg1); 152: } 153: fclose(fp); 154: tcpd_context = saved_context; 155: return (conf); 156: } 157: 158: /* inet_chk - examine one inetd.conf (tlid.conf?) entry */ 159: 160: static void inet_chk(protocol, path, arg0, arg1) 161: char *protocol; 162: char *path; 163: char *arg0; 164: char *arg1; 165: { 166: char daemon[BUFSIZ]; 167: struct stat st; 168: int wrap_status = WR_MAYBE; 169: char *base_name_path = base_name(path); 170: char *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0); 171: 172: /* 173: * Always warn when the executable does not exist or when it is not 174: * executable. 175: */ 176: if (check_path(path, &st) < 0) { 177: tcpd_warn("%s: not found: %m", path); 178: } else if ((st.st_mode & 0100) == 0) { 179: tcpd_warn("%s: not executable", path); 180: } 181: 182: /* 183: * Cheat on the miscd tests, nobody uses it anymore. 184: */ 185: if (STR_EQ(base_name_path, "miscd")) { 186: inet_set(arg0, WR_YES); 187: return; 188: } 189: 190: /* 191: * While we are here... 192: */ 193: if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd")) 194: tcpd_warn("%s may be an insecure service", tcpd_proc_name); 195: 196: /* 197: * The tcpd program gets most of the attention. 198: */ 199: if (STR_EQ(base_name_path, "tcpd")) { 200: 201: wrap_status = WR_YES; 202: 203: /* 204: * Check: some sites install the wrapper set-uid. 205: */ 206: if ((st.st_mode & 06000) != 0) 207: tcpd_warn("%s: file is set-uid or set-gid", path); 208: 209: /* 210: * Check: some sites insert tcpd in inetd.conf, instead of replacing 211: * the daemon pathname. 212: */ 213: if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1))) 214: tcpd_warn("%s inserted before %s", path, arg0); 215: 216: /* 217: * Check: make sure files exist and are executable. On some systems 218: * the network daemons are set-uid so we cannot complain. Note that 219: * tcpd takes the basename only in case of absolute pathnames. 220: */ 221: if (arg0[0] == '/') { /* absolute path */ 222: if (check_path(arg0, &st) < 0) { 223: tcpd_warn("%s: not found: %m", arg0); 224: } else if ((st.st_mode & 0100) == 0) { 225: tcpd_warn("%s: not executable", arg0); 226: } 227: } else { /* look in REAL_DAEMON_DIR */ 228: sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); 229: if (check_path(daemon, &st) < 0) { 230: tcpd_warn("%s: not found in %s: %m", 231: arg0, REAL_DAEMON_DIR); 232: } else if ((st.st_mode & 0100) == 0) { 233: tcpd_warn("%s: not executable", daemon); 234: } 235: } 236: 237: } else { 238: 239: /* 240: * No tcpd program found. Perhaps they used the "simple installation" 241: * recipe. Look for a file with the same basename in REAL_DAEMON_DIR. 242: * Draw some conservative conclusions when a distinct file is found. 243: */ 244: sprintf(daemon, "%s/%s", REAL_DAEMON_DIR, arg0); 245: if (STR_EQ(path, daemon)) { 246: wrap_status = WR_NOT; 247: } else if (check_path(daemon, &st) >= 0) { 248: wrap_status = WR_MAYBE; 249: } else if (errno == ENOENT) { 250: wrap_status = WR_NOT; 251: } else { 252: tcpd_warn("%s: file lookup: %m", daemon); 253: wrap_status = WR_MAYBE; 254: } 255: } 256: 257: /* 258: * Alas, we cannot wrap rpc/tcp services. 259: */ 260: if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp")) 261: tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name); 262: 263: inet_set(tcpd_proc_name, wrap_status); 264: } 265: 266: /* inet_set - remember service status */ 267: 268: void inet_set(name, type) 269: char *name; 270: int type; 271: { 272: struct inet_ent *ip = 273: (struct inet_ent *) malloc(sizeof(struct inet_ent) + strlen(name)); 274: 275: if (ip == 0) { 276: fprintf(stderr, "out of memory\n"); 277: exit(1); 278: } 279: ip->next = inet_list; 280: strcpy(ip->name, name); 281: ip->type = type; 282: inet_list = ip; 283: } 284: 285: /* inet_get - look up service status */ 286: 287: int inet_get(name) 288: char *name; 289: { 290: struct inet_ent *ip; 291: 292: if (inet_list == 0) 293: return (WR_MAYBE); 294: 295: for (ip = inet_list; ip; ip = ip->next) 296: if (STR_EQ(ip->name, name)) 297: return (ip->type); 298: 299: return (-1); 300: } 301: 302: /* base_name - compute last pathname component */ 303: 304: static char *base_name(path) 305: char *path; 306: { 307: char *cp; 308: 309: if ((cp = strrchr(path, '/')) != 0) 310: path = cp + 1; 311: return (path); 312: }