[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