From 0b847172bb5413d200da01618402897d493f40b1 Mon Sep 17 00:00:00 2001 From: Olaf Kirch Date: Tue, 30 Sep 2008 14:49:21 -0400 Subject: 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 Signed-off-by: Steve Dickson --- src/pmap_svc.c | 30 ++++++++++++++++-------------- src/rpcbind.h | 1 + src/security.c | 37 +++++++++++++++++++++++++++++++++++++ 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 *)®)) { 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 -- cgit v1.2.1