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