diff options
author | dwmalone <dwmalone@FreeBSD.org> | 2001-06-16 18:54:54 +0000 |
---|---|---|
committer | dwmalone <dwmalone@FreeBSD.org> | 2001-06-16 18:54:54 +0000 |
commit | 229831d19f3c3478e62ef382202f3da4cee2b0c1 (patch) | |
tree | 26042e92d7262a06440395c55be13a2795624e5d | |
parent | 68be106995a68a6e76ce3951bb8742dde6f57485 (diff) | |
download | FreeBSD-src-229831d19f3c3478e62ef382202f3da4cee2b0c1.zip FreeBSD-src-229831d19f3c3478e62ef382202f3da4cee2b0c1.tar.gz |
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
-rw-r--r-- | usr.sbin/inetd/inetd.8 | 70 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.c | 105 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.h | 6 |
3 files changed, 169 insertions, 12 deletions
diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 index 937aa8d..5b0b30b 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 eca4749..61945dc 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 <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 @@ more: /* 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 @@ more: 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 @@ more: 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 f54447f..7790880 100644 --- a/usr.sbin/inetd/inetd.h +++ b/usr.sbin/inetd/inetd.h @@ -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 */ |