Give inetd the ability to manage unix domain sockets. Details of

how to use this feature are in the man page. This is based on work
by Lyndon Nerenberg.

(The only difficult part about this patch is the fact that you
can't fchown a unix domain socket, which means the sockets must be
put in a secure directory).

Reviewed by:	dillon
This commit is contained in:
David Malone 2001-06-16 18:54:54 +00:00
parent dc6af80701
commit 1c8d1174b7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=78356
3 changed files with 169 additions and 12 deletions

View File

@ -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
#@

View File

@ -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 <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@ -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))

View File

@ -35,6 +35,7 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
@ -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 */