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