diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 index 937aa8db2e79..5b0b30b61a05 100644 --- a/usr.sbin/inetd/inetd.8 +++ b/usr.sbin/inetd/inetd.8 @@ -200,7 +200,10 @@ The .Em service-name entry is the name of a valid service in the file -.Pa /etc/services . +.Pa /etc/services , +or the specification of a +.Ux +domain socket (see below). For .Dq internal services (discussed below), the service @@ -250,7 +253,8 @@ TCPMUX services must use .Pp The .Em protocol -must be a valid protocol. +must be a valid protocol or +.Dq unix . Examples are .Dq tcp or @@ -580,6 +584,7 @@ records its process ID in the file .Pa /var/run/inetd.pid to assist in reconfiguration. .Sh IMPLEMENTATION NOTES +.Ss TCP Wrappers When given the .Fl w option, @@ -682,6 +687,66 @@ If an invalid IPsec policy specifier appears in will provide an error message via the .Xr syslog 3 interface and abort execution. +.Ss Ux Domain Sockets +In addition to running services on IP sockets, +.Nm +can also manage +.Ux +domain sockets. +To do this you specify a +.Em protocol +of +.Dq unix +and specify the unix domain socket as the +.Em service-name . +The +.Em service-type +may be +.Dq stream +or +.Dq dgram . +The specification of the socket must be +an absolute path name, +optionally prefixed by an owner and mode +of the form +.Em :user:group:mode: . +The specification: +.Bd -literal -offset indent -compact +:news:daemon:220:/var/run/sock +.Ed +creates a socket owned +by user news in group daemon +with permissions allowing only that user and group to connect. +The default owner is the user that inetd is running as. +The default mode only allows the socket's owner to connect. +.Pp +.Sy WARNING: +while creating +.Ux +domain socket +.Nm +must change the ownership and permissions on the socket. +This can only be done securely if +the directory in which the socket is created +is writable only by root. +Do +.Sy NOT +use +.Nm +to create sockets in world writable directories, +such as +.Pa /tmp , +instead use +.Pa /var/run +or a similar directory. +.Pp +Internal services may be run on +.Ux +domain sockets, in the usual way. +In this case +the name of the internal service +is determined using +the last component of the socket's pathname. .Sh "FILES" .Bl -tag -width /var/run/inetd.pid -compact .It Pa /etc/inetd.conf @@ -705,6 +770,7 @@ shell stream tcp46 nowait root /usr/libexec/rshd rshd tcpmux/+date stream tcp nowait guest /bin/date date tcpmux/phonebook stream tcp nowait guest /usr/local/bin/phonebook phonebook rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd +/var/run/echo stream unix nowait root internal #@ ipsec ah/require chargen stream tcp nowait root internal #@ diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c index eca4749603e8..61945dc3d7c8 100644 --- a/usr.sbin/inetd/inetd.c +++ b/usr.sbin/inetd/inetd.c @@ -68,10 +68,11 @@ static const char rcsid[] = * order shown below. Continuation lines for an entry must begin with * a space or tab. All fields must be present in each entry. * - * service name must be in /etc/services or must - * name a tcpmux service + * service name must be in /etc/services + * or name a tcpmux service + * or specify a unix domain socket * socket type stream/dgram/raw/rdm/seqpacket - * protocol tcp[4][6][/faith,ttcp], udp[4][6] + * protocol tcp[4][6][/faith,ttcp], udp[4][6], unix * wait/nowait single-threaded/multi-threaded * user user to run daemon as * server program full path name @@ -115,6 +116,8 @@ static const char rcsid[] = #include #include #include +#include +#include #include #include @@ -169,6 +172,7 @@ static const char rcsid[] = #define ISWRAP(sep) \ ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \ + && (sep->se_family == AF_INET || sep->se_family == AF_INET6) \ && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \ || (sep)->se_socktype == SOCK_DGRAM)) @@ -226,6 +230,9 @@ int signalpipe[2]; #ifdef SANITY_CHECK int nsock; #endif +uid_t euid; +gid_t egid; +mode_t mask; struct servtab *servtab; @@ -403,6 +410,10 @@ main(argc, argv, envp) exit(EX_USAGE); } + euid = geteuid(); + egid = getegid(); + umask(mask = umask(0777)); + argc -= optind; argv += optind; @@ -898,6 +909,7 @@ void config() for (sep = servtab; sep; sep = sep->se_next) if (strcmp(sep->se_service, new->se_service) == 0 && strcmp(sep->se_proto, new->se_proto) == 0 && + sep->se_socktype == new->se_socktype && sep->se_family == new->se_family) break; if (sep != 0) { @@ -978,12 +990,14 @@ void config() #endif } if (!sep->se_rpc) { - sp = getservbyname(sep->se_service, sep->se_proto); - if (sp == 0) { - syslog(LOG_ERR, "%s/%s: unknown service", - sep->se_service, sep->se_proto); - sep->se_checked = 0; - continue; + if (sep->se_family != AF_UNIX) { + sp = getservbyname(sep->se_service, sep->se_proto); + if (sp == 0) { + syslog(LOG_ERR, "%s/%s: unknown service", + sep->se_service, sep->se_proto); + sep->se_checked = 0; + continue; + } } switch (sep->se_family) { case AF_INET: @@ -1154,6 +1168,10 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) #ifdef IPSEC ipsecsetup(sep); #endif + if (sep->se_family == AF_UNIX) { + (void) unlink(sep->se_ctrladdr_un.sun_path); + umask(0777); /* Make socket with conservative permissions */ + } if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { if (debug) @@ -1167,8 +1185,18 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) timingout = 1; alarm(RETRYTIME); } + if (sep->se_family == AF_UNIX) + umask(mask); return; } + if (sep->se_family == AF_UNIX) { + /* Ick - fch{own,mod} don't work on Unix domain sockets */ + if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0) + syslog(LOG_ERR, "chown socket: %m"); + if (chmod(sep->se_service, sep->se_sockmode) < 0) + syslog(LOG_ERR, "chmod socket: %m"); + umask(mask); + } if (sep->se_rpc) { int i; socklen_t len = sep->se_ctrladdr_size; @@ -1301,9 +1329,15 @@ int matchservent(name1, name2, proto) char *name1, *name2, *proto; { - char **alias; + char **alias, *p; struct servent *se; + if (strcmp(proto, "unix") == 0) { + if ((p = strrchr(name1, '/')) != NULL) + name1 = p + 1; + if ((p = strrchr(name2, '/')) != NULL) + name2 = p + 1; + } if (strcmp(name1, name2) == 0) return(1); if ((se = getservbyname(name1, proto)) != NULL) { @@ -1483,6 +1517,42 @@ getconfigent() /* got an empty line containing just blanks/tabs. */ goto more; } + if (arg[0] == ':') { /* :user:group:perm: */ + char *user, *group, *perm; + struct passwd *pw; + struct group *gr; + user = arg+1; + if ((group = strchr(user, ':')) == NULL) { + syslog(LOG_ERR, "no group after user '%s'", user); + goto more; + } + *group++ = '\0'; + if ((perm = strchr(group, ':')) == NULL) { + syslog(LOG_ERR, "no mode after group '%s'", group); + goto more; + } + *perm++ = '\0'; + if ((pw = getpwnam(user)) == NULL) { + syslog(LOG_ERR, "no such user '%s'", user); + goto more; + } + sep->se_sockuid = pw->pw_uid; + if ((gr = getgrnam(group)) == NULL) { + syslog(LOG_ERR, "no such user '%s'", group); + goto more; + } + sep->se_sockgid = gr->gr_gid; + sep->se_sockmode = strtol(perm, &arg, 8); + if (*arg != ':') { + syslog(LOG_ERR, "bad mode '%s'", perm); + goto more; + } + *arg++ = '\0'; + } else { + sep->se_sockuid = euid; + sep->se_sockgid = egid; + sep->se_sockmode = 0200; + } if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { char *c = arg + MUX_LEN; if (*c == '+') { @@ -1589,6 +1659,9 @@ getconfigent() freeconfig(sep); goto more; } + if (strcmp(sep->se_proto, "unix") == 0) { + sep->se_family = AF_UNIX; + } else #ifdef INET6 if (v6bind != 0) { sep->se_family = AF_INET6; @@ -1619,6 +1692,18 @@ getconfigent() sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); break; #endif + case AF_UNIX: + if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) { + syslog(LOG_ERR, + "domain socket pathname too long for service %s", + sep->se_service); + goto more; + } + memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr)); + sep->se_ctrladdr_un.sun_family = sep->se_family; + sep->se_ctrladdr_un.sun_len = strlen(sep->se_service); + strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service); + sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un); } arg = sskip(&cp); if (!strncmp(arg, "wait", 4)) diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h index f54447ffe9ef..7790880ecf29 100644 --- a/usr.sbin/inetd/inetd.h +++ b/usr.sbin/inetd/inetd.h @@ -35,6 +35,7 @@ #include #include +#include #include @@ -80,11 +81,16 @@ struct servtab { struct sockaddr se_un_ctrladdr; struct sockaddr_in se_un_ctrladdr4; struct sockaddr_in6 se_un_ctrladdr6; + struct sockaddr_un se_un_ctrladdr_un; } se_un; #define se_ctrladdr se_un.se_un_ctrladdr #define se_ctrladdr4 se_un.se_un_ctrladdr4 #define se_ctrladdr6 se_un.se_un_ctrladdr6 +#define se_ctrladdr_un se_un.se_un_ctrladdr_un socklen_t se_ctrladdr_size; + uid_t se_sockuid; /* Owner for unix domain socket */ + gid_t se_sockgid; /* Group for unix domain socket */ + mode_t se_sockmode; /* Mode for unix domain socket */ u_char se_type; /* type: normal, mux, or mux+ */ u_char se_checked; /* looked at during merge */ u_char se_accept; /* i.e., wait/nowait mode */