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