Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lockd: Add helper to sanity check incoming NOTIFY requests

lockd accepts SM_NOTIFY calls only from a privileged process on the
local system. If lockd uses an AF_INET6 listener, the sender's address
(ie the local rpc.statd) will be the IPv6 loopback address, not the
IPv4 loopback address.

Make sure the privilege test in nlmsvc_proc_sm_notify() and
nlm4svc_proc_sm_notify() works for both AF_INET and AF_INET6 family
addresses by refactoring the test into a helper and adding support for
IPv6 addresses.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

authored by

Chuck Lever and committed by
J. Bruce Fields
b85e4676 dcff09f1

+45 -8
+2 -4
fs/lockd/svc4proc.c
··· 421 421 { 422 422 struct sockaddr_in saddr; 423 423 424 - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); 425 - 426 424 dprintk("lockd: SM_NOTIFY called\n"); 427 - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) 428 - || ntohs(saddr.sin_port) >= 1024) { 425 + 426 + if (!nlm_privileged_requester(rqstp)) { 429 427 char buf[RPC_MAX_ADDRBUFLEN]; 430 428 printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", 431 429 svc_print_addr(rqstp, buf, sizeof(buf)));
+2 -4
fs/lockd/svcproc.c
··· 453 453 { 454 454 struct sockaddr_in saddr; 455 455 456 - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); 457 - 458 456 dprintk("lockd: SM_NOTIFY called\n"); 459 - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) 460 - || ntohs(saddr.sin_port) >= 1024) { 457 + 458 + if (!nlm_privileged_requester(rqstp)) { 461 459 char buf[RPC_MAX_ADDRBUFLEN]; 462 460 printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", 463 461 svc_print_addr(rqstp, buf, sizeof(buf)));
+41
include/linux/lockd/lockd.h
··· 277 277 return file->f_file->f_path.dentry->d_inode; 278 278 } 279 279 280 + static inline int __nlm_privileged_request4(const struct sockaddr *sap) 281 + { 282 + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; 283 + return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && 284 + (ntohs(sin->sin_port) < 1024); 285 + } 286 + 287 + #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 288 + static inline int __nlm_privileged_request6(const struct sockaddr *sap) 289 + { 290 + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; 291 + return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) && 292 + (ntohs(sin6->sin6_port) < 1024); 293 + } 294 + #else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ 295 + static inline int __nlm_privileged_request6(const struct sockaddr *sap) 296 + { 297 + return 0; 298 + } 299 + #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ 300 + 301 + /* 302 + * Ensure incoming requests are from local privileged callers. 303 + * 304 + * Return TRUE if sender is local and is connecting via a privileged port; 305 + * otherwise return FALSE. 306 + */ 307 + static inline int nlm_privileged_requester(const struct svc_rqst *rqstp) 308 + { 309 + const struct sockaddr *sap = svc_addr(rqstp); 310 + 311 + switch (sap->sa_family) { 312 + case AF_INET: 313 + return __nlm_privileged_request4(sap); 314 + case AF_INET6: 315 + return __nlm_privileged_request6(sap); 316 + default: 317 + return 0; 318 + } 319 + } 320 + 280 321 static inline int __nlm_cmp_addr4(const struct sockaddr *sap1, 281 322 const struct sockaddr *sap2) 282 323 {