[PATCH] Use credentials and permissions on control socket where available

Scott Lamb slamb at slamb.org
Thu Oct 18 19:36:31 CEST 2007


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"



More information about the tinc-devel mailing list