summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlaf Kirch <okir@suse.de>2008-09-30 14:49:21 -0400
committerSteve Dickson <steved@redhat.com>2008-09-30 14:49:21 -0400
commit0b847172bb5413d200da01618402897d493f40b1 (patch)
treea8a61f9a2f3b0c5178d1af1fdab393d3d3405e9b
parent49e8a7c963c27ca3face271200b103a5efc50b05 (diff)
downloadrpcbind-0b847172bb5413d200da01618402897d493f40b1.tar.gz
Properly identify local root user over ipv4/v6
When an application registers a service through an inet transport, rpcbind will always treat the owner as "unknown". This allows random users to unregister such services, and replace them with their own - man-in-the-middle attacks for services like ypserv are trivial. This patch changes pmapproc_change to check whether the call originated from a priviliged local port, and if that is the case, it identifies the caller as "superuser". This mimics the way the current Linux portmap behaves. Signed-off-by: Olaf Kirch <okir@suse.de> Signed-off-by: Steve Dickson <steved@redhat.com>
-rw-r--r--src/pmap_svc.c30
-rw-r--r--src/rpcbind.h1
-rw-r--r--src/security.c37
3 files changed, 54 insertions, 14 deletions
diff --git a/src/pmap_svc.c b/src/pmap_svc.c
index 6562687..bb0aaa9 100644
--- a/src/pmap_svc.c
+++ b/src/pmap_svc.c
@@ -175,6 +175,22 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned long
uid_t uid;
char uidbuf[32];
+ /*
+ * Can't use getpwnam here. We might end up calling ourselves
+ * and looping.
+ */
+ if (__rpc_get_local_uid(xprt, &uid) < 0) {
+ rpcbreg.r_owner = "unknown";
+ if (is_localroot(svc_getrpccaller(xprt)))
+ rpcbreg.r_owner = "superuser";
+ } else if (uid == 0)
+ rpcbreg.r_owner = "superuser";
+ else {
+ /* r_owner will be strdup-ed later */
+ snprintf(uidbuf, sizeof uidbuf, "%d", uid);
+ rpcbreg.r_owner = uidbuf;
+ }
+
if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)&reg)) {
svcerr_decode(xprt);
return (FALSE);
@@ -191,20 +207,6 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned long
return (FALSE);
}
- /*
- * Can't use getpwnam here. We might end up calling ourselves
- * and looping.
- */
- if (__rpc_get_local_uid(xprt, &uid) < 0)
- rpcbreg.r_owner = "unknown";
- else if (uid == 0)
- rpcbreg.r_owner = "superuser";
- else {
- /* r_owner will be strdup-ed later */
- snprintf(uidbuf, sizeof uidbuf, "%d", uid);
- rpcbreg.r_owner = uidbuf;
- }
-
rpcbreg.r_prog = reg.pm_prog;
rpcbreg.r_vers = reg.pm_vers;
diff --git a/src/rpcbind.h b/src/rpcbind.h
index 295711a..8fd9703 100644
--- a/src/rpcbind.h
+++ b/src/rpcbind.h
@@ -124,6 +124,7 @@ int check_access(SVCXPRT *, rpcproc_t, void *, unsigned int);
int check_callit(SVCXPRT *, struct r_rmtcall_args *, int);
void logit(int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *);
int is_loopback(struct netbuf *);
+int is_localroot(struct netbuf *);
#ifdef PORTMAP
extern void pmap_service(struct svc_req *, SVCXPRT *);
diff --git a/src/security.c b/src/security.c
index 6fc4280..0edeac6 100644
--- a/src/security.c
+++ b/src/security.c
@@ -177,6 +177,43 @@ is_loopback(struct netbuf *nbuf)
return 0;
}
+/*
+ * For IPv4/v6, this is exactly the same as is_loopback for now.
+ * The difference is that this returns false for other transports.
+ */
+int
+is_localroot(struct netbuf *nbuf)
+{
+ struct sockaddr *addr = (struct sockaddr *)nbuf->buf;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ if (!oldstyle_local)
+ return 0;
+ sin = (struct sockaddr_in *)addr;
+ return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
+ (ntohs(sin->sin_port) < IPPORT_RESERVED));
+#ifdef INET6
+ case AF_INET6:
+ if (!oldstyle_local)
+ return 0;
+ sin6 = (struct sockaddr_in6 *)addr;
+ return ((IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ||
+ (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) &&
+ sin6->sin6_addr.s6_addr32[3] == htonl(INADDR_LOOPBACK))) &&
+ (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED));
+#endif
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/* logit - report events of interest via the syslog daemon */
void