From 903e91b41b4c4e72febb2e8342343a0cd37cbe30 Mon Sep 17 00:00:00 2001 From: deraadt Date: Mon, 22 Jan 2024 16:18:06 +0000 Subject: [PATCH] When getpwnam(3) reaches out to YP, it calls clntudp_create(3) with a pre-initialized ypconnect(2) socket. That calls clntudp_bufcreate(), which contains code checking if the socket and address are configured.. If not, socket(2) is called, or an address allocation is performed via the portmapper (which calls a whole lot more code). Split clnt_udp.c into two .c files (which will compile as seperate .o files), and create a new libc-private clntudp_bufcreate_simple() function which skips the socket and address work. Result: In most static binaries, this reduces the text segment by ~100K, and removes 5-7 system call stubs -- which might matter for non-pledged binaries with otherwise lack socket(2). ok millert jmatthew --- lib/libc/rpc/Makefile.inc | 4 +- lib/libc/rpc/clnt_udp.c | 195 +++++++++++++----------------- lib/libc/rpc/clnt_udp.h | 70 +++++++++++ lib/libc/rpc/clnt_udp_bufcreate.c | 130 ++++++++++++++++++++ lib/libc/yp/yp_bind.c | 10 +- 5 files changed, 296 insertions(+), 113 deletions(-) create mode 100644 lib/libc/rpc/clnt_udp.h create mode 100644 lib/libc/rpc/clnt_udp_bufcreate.c diff --git a/lib/libc/rpc/Makefile.inc b/lib/libc/rpc/Makefile.inc index 940fa4157aa..f8b1f6339ab 100644 --- a/lib/libc/rpc/Makefile.inc +++ b/lib/libc/rpc/Makefile.inc @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile.inc,v 1.18 2016/03/30 06:38:41 jmc Exp $ +# $OpenBSD: Makefile.inc,v 1.19 2024/01/22 16:18:06 deraadt Exp $ # librpc sources .PATH: ${LIBCSRCDIR}/arch/${MACHINE}/rpc ${LIBCSRCDIR}/rpc SRCS+= auth_none.c auth_unix.c authunix_prot.c bindresvport.c \ clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c clnt_tcp.c \ - clnt_udp.c get_myaddress.c getrpcent.c getrpcport.c \ + clnt_udp.c clnt_udp_bufcreate.c get_myaddress.c getrpcent.c getrpcport.c \ pmap_clnt.c pmap_getmaps.c pmap_getport.c pmap_prot.c \ pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c rpc_callmsg.c \ svc.c svc_auth.c svc_auth_unix.c svc_raw.c svc_run.c svc_simple.c \ diff --git a/lib/libc/rpc/clnt_udp.c b/lib/libc/rpc/clnt_udp.c index e40347be5c7..bcb5b35adf1 100644 --- a/lib/libc/rpc/clnt_udp.c +++ b/lib/libc/rpc/clnt_udp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clnt_udp.c,v 1.40 2022/08/24 01:32:21 deraadt Exp $ */ +/* $OpenBSD: clnt_udp.c,v 1.41 2024/01/22 16:18:06 deraadt Exp $ */ /* * Copyright (c) 2010, Oracle America, Inc. @@ -44,7 +44,7 @@ #include #include #include -#include +#include "clnt_udp.h" /* * UDP bases client side rpc operations @@ -66,31 +66,65 @@ static const struct clnt_ops udp_ops = { clntudp_control }; -/* - * Private data kept per client handle - */ -struct cu_data { - int cu_sock; - bool_t cu_closeit; - struct sockaddr_in cu_raddr; - int cu_connected; /* use send() instead */ - int cu_rlen; - struct timeval cu_wait; - struct timeval cu_total; - struct rpc_err cu_error; - XDR cu_outxdrs; - u_int cu_xdrpos; - u_int cu_sendsz; - char *cu_outbuf; - u_int cu_recvsz; - char cu_inbuf[1]; -}; +int +clntudp_bufcreate1(struct clntudp_bufcreate_args *args) +{ + args->cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (args->cl == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return -1; + } + args->sendsz = ((args->sendsz + 3) / 4) * 4; + args->recvsz = ((args->recvsz + 3) / 4) * 4; + args->cu = (struct cu_data *)mem_alloc(sizeof(args->cu) + + args->sendsz + args->recvsz); + if (args->cu == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return -1; + } + args->cu->cu_outbuf = &args->cu->cu_inbuf[args->recvsz]; + args->cl->cl_ops = &udp_ops; + args->cl->cl_private = (caddr_t)args->cu; + args->cu->cu_connected = 0; + args->cu->cu_rlen = sizeof (args->cu->cu_raddr); + args->cu->cu_wait = args->wait; + args->cu->cu_total.tv_sec = -1; + args->cu->cu_total.tv_usec = -1; + args->cu->cu_sendsz = args->sendsz; + args->cu->cu_recvsz = args->recvsz; + args->cu->cu_closeit = FALSE; + args->call_msg.rm_xid = arc4random(); + args->call_msg.rm_direction = CALL; + args->call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + args->call_msg.rm_call.cb_prog = args->program; + args->call_msg.rm_call.cb_vers = args->version; + return 0; +} + +int +clntudp_bufcreate2(struct clntudp_bufcreate_args *args) +{ + xdrmem_create(&(args->cu->cu_outxdrs), args->cu->cu_outbuf, + args->sendsz, XDR_ENCODE); + if (!xdr_callhdr(&(args->cu->cu_outxdrs), &args->call_msg)) + return -1; + args->cu->cu_xdrpos = XDR_GETPOS(&(args->cu->cu_outxdrs)); + args->cl->cl_auth = authnone_create(); + if (args->cl->cl_auth == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + return -1; + } + return 0; +} /* * Create a UDP based client handle. - * If *sockp<0, *sockp is set to a newly created UPD socket. + * If *sockp<0, *sockp is set to a newly created UPD socket. (***) * If raddr->sin_port is 0 a binder on the remote machine - * is consulted for the correct port number. + * is consulted for the correct port number. (***) * NB: It is the client's responsibility to close *sockp, unless * clntudp_bufcreate() was called with *sockp = -1 (so it created * the socket), and CLNT_DESTROY() is used. @@ -103,100 +137,45 @@ struct cu_data { * * sendsz and recvsz are the maximum allowable packet sizes that can be * sent and received. + * + * This is a reduced-functionality version of clntudp_bufcreate() that + * does not allocate socket or binding (***, above). + * The official function clntudp_bufcreate(), which does perform those + * two steps, is in clnt_udp_bufcreate.c. This split avoids pulling + * socket / portmap related code into programs only using getpwent / YP code. */ + CLIENT * -clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version, +clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version, struct timeval wait, int *sockp, u_int sendsz, u_int recvsz) { - CLIENT *cl; - struct cu_data *cu = NULL; - struct rpc_msg call_msg; - - cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); - if (cl == NULL) { - rpc_createerr.cf_stat = RPC_SYSTEMERROR; - rpc_createerr.cf_error.re_errno = errno; + struct clntudp_bufcreate_args args; + + args.raddr = raddr; + args.program = program; + args.version = version; + args.wait = wait; + args.sockp = sockp; + args.sendsz = sendsz; + args.recvsz = recvsz; + args.cl = NULL; + args.cu = NULL; + + if (clntudp_bufcreate1(&args) == -1) goto fooy; - } - sendsz = ((sendsz + 3) / 4) * 4; - recvsz = ((recvsz + 3) / 4) * 4; - cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); - if (cu == NULL) { - rpc_createerr.cf_stat = RPC_SYSTEMERROR; - rpc_createerr.cf_error.re_errno = errno; + args.cu->cu_raddr = *raddr; + args.cu->cu_sock = *sockp; + if (clntudp_bufcreate2(&args) == -1) goto fooy; - } - cu->cu_outbuf = &cu->cu_inbuf[recvsz]; - - if (raddr->sin_port == 0) { - u_short port; - if ((port = - pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { - goto fooy; - } - raddr->sin_port = htons(port); - } - cl->cl_ops = &udp_ops; - cl->cl_private = (caddr_t)cu; - cu->cu_raddr = *raddr; - cu->cu_connected = 0; - cu->cu_rlen = sizeof (cu->cu_raddr); - cu->cu_wait = wait; - cu->cu_total.tv_sec = -1; - cu->cu_total.tv_usec = -1; - cu->cu_sendsz = sendsz; - cu->cu_recvsz = recvsz; - call_msg.rm_xid = arc4random(); - call_msg.rm_direction = CALL; - call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; - call_msg.rm_call.cb_prog = program; - call_msg.rm_call.cb_vers = version; - xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, - sendsz, XDR_ENCODE); - if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { - goto fooy; - } - cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); - if (*sockp < 0) { - *sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, - IPPROTO_UDP); - if (*sockp == -1) { - rpc_createerr.cf_stat = RPC_SYSTEMERROR; - rpc_createerr.cf_error.re_errno = errno; - goto fooy; - } - /* attempt to bind to priv port */ - (void)bindresvport(*sockp, NULL); - cu->cu_closeit = TRUE; - } else { - cu->cu_closeit = FALSE; - } - cu->cu_sock = *sockp; - cl->cl_auth = authnone_create(); - if (cl->cl_auth == NULL) { - rpc_createerr.cf_stat = RPC_SYSTEMERROR; - rpc_createerr.cf_error.re_errno = errno; - goto fooy; - } - return (cl); + return (args.cl); fooy: - if (cu) - mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); - if (cl) - mem_free((caddr_t)cl, sizeof(CLIENT)); + if (args.cu) + mem_free((caddr_t)args.cu, + sizeof(*args.cu) + args.sendsz + args.recvsz); + if (args.cl) + mem_free((caddr_t)args.cl, sizeof(CLIENT)); return (NULL); } -DEF_WEAK(clntudp_bufcreate); - -CLIENT * -clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version, - struct timeval wait, int *sockp) -{ - - return(clntudp_bufcreate(raddr, program, version, wait, sockp, - UDPMSGSIZE, UDPMSGSIZE)); -} -DEF_WEAK(clntudp_create); static enum clnt_stat clntudp_call(CLIENT *cl, /* client handle */ diff --git a/lib/libc/rpc/clnt_udp.h b/lib/libc/rpc/clnt_udp.h new file mode 100644 index 00000000000..745540be945 --- /dev/null +++ b/lib/libc/rpc/clnt_udp.h @@ -0,0 +1,70 @@ +/* $OpenBSD: clnt_udp.h,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */ + +/* + * Copyright (c) 2010, Oracle America, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * * Neither the name of the "Oracle America, Inc." nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Private data kept per client handle + */ +struct cu_data { + int cu_sock; + bool_t cu_closeit; + struct sockaddr_in cu_raddr; + int cu_connected; /* use send() instead */ + int cu_rlen; + struct timeval cu_wait; + struct timeval cu_total; + struct rpc_err cu_error; + XDR cu_outxdrs; + u_int cu_xdrpos; + u_int cu_sendsz; + char *cu_outbuf; + u_int cu_recvsz; + char cu_inbuf[1]; +}; + +struct clntudp_bufcreate_args { + struct sockaddr_in *raddr; + u_long program; + u_long version; + struct timeval wait; + int *sockp; + u_int sendsz; + u_int recvsz; + CLIENT *cl; + struct cu_data *cu; + struct rpc_msg call_msg; +}; + +__BEGIN_HIDDEN_DECLS +extern int clntudp_bufcreate1(struct clntudp_bufcreate_args *); +extern int clntudp_bufcreate2(struct clntudp_bufcreate_args *); +__END_HIDDEN_DECLS diff --git a/lib/libc/rpc/clnt_udp_bufcreate.c b/lib/libc/rpc/clnt_udp_bufcreate.c new file mode 100644 index 00000000000..b112c8a1613 --- /dev/null +++ b/lib/libc/rpc/clnt_udp_bufcreate.c @@ -0,0 +1,130 @@ +/* $OpenBSD: clnt_udp_bufcreate.c,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */ + +/* + * Copyright (c) 2010, Oracle America, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * * Neither the name of the "Oracle America, Inc." nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * clnt_udp.c, Implements a UDP/IP based, client side RPC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clnt_udp.h" + +/* + * Create a UDP based client handle. + * If *sockp<0, *sockp is set to a newly created UPD socket. + * If raddr->sin_port is 0 a binder on the remote machine + * is consulted for the correct port number. + * NB: It is the client's responsibility to close *sockp, unless + * clntudp_bufcreate() was called with *sockp = -1 (so it created + * the socket), and CLNT_DESTROY() is used. + * NB: The rpch->cl_auth is initialized to null authentication. + * Caller may wish to set this something more useful. + * + * wait is the amount of time used between retransmitting a call if + * no response has been heard; retransmission occurs until the actual + * rpc call times out. + * + * sendsz and recvsz are the maximum allowable packet sizes that can be + * sent and received. + */ + +CLIENT * +clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version, + struct timeval wait, int *sockp, u_int sendsz, u_int recvsz) +{ + struct clntudp_bufcreate_args args; + + args.raddr = raddr; + args.program = program; + args.version = version; + args.wait = wait; + args.sockp = sockp; + args.sendsz = sendsz; + args.recvsz = recvsz; + + if (clntudp_bufcreate1(&args) == -1) + goto fooy; + + if (raddr->sin_port == 0) { + u_short port; + if ((port = + pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { + goto fooy; + } + raddr->sin_port = htons(port); + } + args.cu->cu_raddr = *raddr; + if (*sockp < 0) { + *sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, + IPPROTO_UDP); + if (*sockp == -1) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + /* attempt to bind to priv port */ + (void)bindresvport(*sockp, NULL); + args.cu->cu_closeit = TRUE; + } + args.cu->cu_sock = *args.sockp; + + if (clntudp_bufcreate2(&args) == -1) + goto fooy; + return (args.cl); +fooy: + if (args.cu) + mem_free((caddr_t)args.cu, + sizeof(*args.cu) + args.sendsz + args.recvsz); + if (args.cl) + mem_free((caddr_t)args.cl, sizeof(CLIENT)); + return (NULL); +} +DEF_WEAK(clntudp_bufcreate); + +CLIENT * +clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version, + struct timeval wait, int *sockp) +{ + + return(clntudp_bufcreate(raddr, program, version, wait, sockp, + UDPMSGSIZE, UDPMSGSIZE)); +} +DEF_WEAK(clntudp_create); diff --git a/lib/libc/yp/yp_bind.c b/lib/libc/yp/yp_bind.c index af6775e96b8..d99a5962bdd 100644 --- a/lib/libc/yp/yp_bind.c +++ b/lib/libc/yp/yp_bind.c @@ -1,4 +1,4 @@ -/* $OpenBSD: yp_bind.c,v 1.32 2022/08/02 16:59:29 deraadt Exp $ */ +/* $OpenBSD: yp_bind.c,v 1.33 2024/01/22 16:18:06 deraadt Exp $ */ /* * Copyright (c) 1992, 1993, 1996 Theo de Raadt * All rights reserved. @@ -46,6 +46,10 @@ char _yp_domain[HOST_NAME_MAX+1]; int _yplib_timeout = 10; +extern CLIENT * +clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version, + struct timeval wait, int *sockp, u_int sendsz, u_int recvsz); + int _yp_dobind(const char *dom, struct dom_binding **ypdb) { @@ -72,8 +76,8 @@ again: tv.tv_sec = _yplib_timeout / 2; tv.tv_usec = 0; - ypbinding->dom_client = clntudp_create(&ypbinding->dom_server_addr, - YPPROG, YPVERS, tv, &ypbinding->dom_socket); + ypbinding->dom_client = clntudp_bufcreate_simple(&ypbinding->dom_server_addr, + YPPROG, YPVERS, tv, &ypbinding->dom_socket, UDPMSGSIZE, UDPMSGSIZE); if (ypbinding->dom_client == NULL) { close(ypbinding->dom_socket); ypbinding->dom_socket = -1; -- 2.20.1