Scott Lamb
2007-Oct-18 17:36 UTC
[PATCH] Use credentials and permissions on control socket where available
There are at least three cases: * Linux: check credentials and pid from client; restrict permissions from server * BSD: check credentials only from client; restrict permissions from server * Solaris: wide open --- configure.in | 4 ++-- src/control.c | 11 ++++++++++- src/control_common.h | 1 + src/tincctl.c | 38 ++++++++++++++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 670e855..6b13eb3 100644 --- a/configure.in +++ b/configure.in @@ -114,7 +114,7 @@ AC_STRUCT_TM tinc_ATTRIBUTE(__malloc__) -AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp, struct in_addr, struct addrinfo, struct ip, struct icmp, struct in6_addr, struct sockaddr_in6, struct ip6_hdr, struct icmp6_hdr, struct nd_neighbor_solicit, struct nd_opt_hdr], , , +AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp, struct in_addr, struct addrinfo, struct ip, struct icmp, struct in6_addr, struct sockaddr_in6, struct ip6_hdr, struct icmp6_hdr, struct nd_neighbor_solicit, struct nd_opt_hdr, struct ucred], , , [#include "have.h"] ) @@ -122,7 +122,7 @@ dnl Checks for library functions. AC_FUNC_MEMCMP AC_FUNC_ALLOCA AC_TYPE_SIGNAL -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system unsetenv vsyslog writev], +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name getpeereid gettimeofday mlockall putenv random select strdup strerror strsignal strtol system unsetenv vsyslog writev], [], [], [#include "have.h"] ) AC_FUNC_MALLOC diff --git a/src/control.c b/src/control.c index a795843..4454126 100644 --- a/src/control.c +++ b/src/control.c @@ -191,6 +191,7 @@ static void handle_new_control_socket(int fd, short events, void *data) { memset(&greeting, 0, sizeof greeting); greeting.version = TINC_CTL_VERSION_CURRENT; + greeting.pid = getpid(); if(bufferevent_write(ev, &greeting, sizeof greeting) == -1) { logger(LOG_ERR, _("Cannot send greeting for new control connection: %s"), @@ -213,6 +214,7 @@ static int control_compare(const struct event *a, const struct event *b) { bool init_control() { int result; struct sockaddr_un addr; + mode_t old_umask; if(strlen(controlsocketname) >= sizeof addr.sun_path) { logger(LOG_ERR, _("Control socket filename too long!")); @@ -230,7 +232,11 @@ bool init_control() { return false; } - //unlink(controlsocketname); + /* + * Restrict access to the control socket. + * I believe this works everywhere but Solaris. + */ + old_umask = umask(0077); result = bind(control_socket, (struct sockaddr *)&addr, sizeof addr); if(result < 0 && errno == EADDRINUSE) { @@ -245,10 +251,13 @@ bool init_control() { logger(LOG_ERR, _("Another tincd is already running for net `%s'."), netname); else logger(LOG_ERR, _("Another tincd is already running.")); + umask(old_umask); return false; } } + umask(old_umask); + if(result < 0) { logger(LOG_ERR, _("Can't bind to %s: %s\n"), controlsocketname, strerror(errno)); close(control_socket); diff --git a/src/control_common.h b/src/control_common.h index 0975826..6384651 100644 --- a/src/control_common.h +++ b/src/control_common.h @@ -41,6 +41,7 @@ enum request_type { /* This greeting is sent by the server on socket open. */ typedef struct tinc_ctl_greeting_t { int version; + pid_t pid; } tinc_ctl_greeting_t; /* A single request or response header. */ diff --git a/src/tincctl.c b/src/tincctl.c index 7e08629..6aa1e3e 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -443,6 +443,9 @@ int main(int argc, char *argv[], char *envp[]) { int len; tinc_ctl_greeting_t greeting; tinc_ctl_request_t req; + int result; + struct stat statbuf; + char resolved_path[PATH_MAX], pathtmp[PATH_MAX]; program_name = argv[0]; @@ -493,7 +496,7 @@ int main(int argc, char *argv[], char *envp[]) { // Now handle commands that do involve connecting to a running tinc daemon. - if(strlen(controlsocketname) >= sizeof addr.sun_path) { + if(strlen(resolved_path) >= sizeof addr.sun_path) { fprintf(stderr, _("Control socket filename too long!\n")); return 1; } @@ -509,7 +512,7 @@ int main(int argc, char *argv[], char *envp[]) { strncpy(addr.sun_path, controlsocketname, sizeof addr.sun_path - 1); if(connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) { - fprintf(stderr, _("Cannot connect to %s: %s\n"), controlsocketname, strerror(errno)); + fprintf(stderr, _("Cannot connect to %s: %s\n"), addr.sun_path, strerror(errno)); return 1; } @@ -525,16 +528,43 @@ int main(int argc, char *argv[], char *envp[]) { return 1; } +#if defined(HAVE_STRUCT_UCRED) struct ucred cred; socklen_t credlen = sizeof cred; if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credlen) < 0) { - fprintf(stderr, _("Could not obtain PID: %s\n"), strerror(errno)); + fprintf(stderr, _("Could not establish peer credentials: %s\n"), strerror(errno)); return 1; } + if(cred.uid != 0) { + fprintf(stderr, _("Peer is not root")); + return 1; + } +#elif defined(HAVE_GETPEEREID) + uid_t peer_euid; + gid_t peer_egid; + + if(getpeereid(fd, &peer_euid, &peer_egid) != 0) { + fprintf(stderr, _("Could not establish peer credentials: %s\n"), strerror(errno)); + return 1; + } + if(peer_euid != 0) { + fprintf(stderr, _("Peer is not root")); + return 1; + } +#else + fprintf(stderr, _("Could not establish peer credentials: %s\n"), strerror(ENOSYS)); + /* Proceed anyway, lacking a better way. */ +#endif if(!strcasecmp(argv[optind], "pid")) { - printf("%d\n", cred.pid); +#if defined(HAVE_STRUCT_UCRED) + if(cred.pid != greeting.pid) { + fprintf(stderr, _("Peer claims to have pid %d; credentials indicate %d\n"), greeting.pid, cred.pid); + return 1; + } +#endif + printf("%d\n", greeting.pid); /* trusted due to permission check above */ return 0; } -- 1.5.3.4.395.g85b0 --------------030901060304060800070308 Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0"; name="control-socket-dir.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="control-socket-dir.patch"