122f7cecaSroy /* SPDX-License-Identifier: BSD-2-Clause */
222f7cecaSroy /*
322f7cecaSroy  * Privilege Separation for dhcpcd, BSD driver
43ee74c9aSroy  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
522f7cecaSroy  * All rights reserved
622f7cecaSroy 
722f7cecaSroy  * Redistribution and use in source and binary forms, with or without
822f7cecaSroy  * modification, are permitted provided that the following conditions
922f7cecaSroy  * are met:
1022f7cecaSroy  * 1. Redistributions of source code must retain the above copyright
1122f7cecaSroy  *    notice, this list of conditions and the following disclaimer.
1222f7cecaSroy  * 2. Redistributions in binary form must reproduce the above copyright
1322f7cecaSroy  *    notice, this list of conditions and the following disclaimer in the
1422f7cecaSroy  *    documentation and/or other materials provided with the distribution.
1522f7cecaSroy  *
1622f7cecaSroy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1722f7cecaSroy  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1822f7cecaSroy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1922f7cecaSroy  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2022f7cecaSroy  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2122f7cecaSroy  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2222f7cecaSroy  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2322f7cecaSroy  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2422f7cecaSroy  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2522f7cecaSroy  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2622f7cecaSroy  * SUCH DAMAGE.
2722f7cecaSroy  */
2822f7cecaSroy 
2922f7cecaSroy #include <sys/ioctl.h>
303ee74c9aSroy #include <sys/types.h>
313ee74c9aSroy #include <sys/sysctl.h>
3222f7cecaSroy 
3354b96bebSroy /* Need these for filtering the ioctls */
34d302d004Sroy #include <arpa/inet.h>
3554b96bebSroy #include <net/if.h>
36d302d004Sroy #include <netinet/if_ether.h>
3754b96bebSroy #include <netinet/in.h>
3854b96bebSroy #include <netinet6/in6_var.h>
3954b96bebSroy #include <netinet6/nd6.h>
40d302d004Sroy #ifdef __NetBSD__
41d302d004Sroy #include <netinet/if_ether.h>
42d302d004Sroy #include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */
43d302d004Sroy #elif defined(__DragonFly__)
44d302d004Sroy #include <net/vlan/if_vlan_var.h>
45d302d004Sroy #else
46d302d004Sroy #include <net/if_vlan_var.h>
47d302d004Sroy #endif
4854b96bebSroy #ifdef __DragonFly__
4954b96bebSroy #  include <netproto/802_11/ieee80211_ioctl.h>
5054b96bebSroy #else
5154b96bebSroy #  include <net80211/ieee80211.h>
5254b96bebSroy #  include <net80211/ieee80211_ioctl.h>
5354b96bebSroy #endif
5454b96bebSroy 
5522f7cecaSroy #include <errno.h>
563ee74c9aSroy #include <stdlib.h>
5754b96bebSroy #include <string.h>
5822f7cecaSroy #include <unistd.h>
5922f7cecaSroy 
6022f7cecaSroy #include "dhcpcd.h"
613ee74c9aSroy #include "if.h"
6222f7cecaSroy #include "logerr.h"
6322f7cecaSroy #include "privsep.h"
6422f7cecaSroy 
6522f7cecaSroy static ssize_t
ps_root_doioctldom(struct dhcpcd_ctx * ctx,int domain,unsigned long req,void * data,size_t len)663ee74c9aSroy ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len)
6722f7cecaSroy {
683ee74c9aSroy #if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))
693ee74c9aSroy 	struct priv *priv = (struct priv *)ctx->priv;
703ee74c9aSroy #endif
713ee74c9aSroy 	int s;
723ee74c9aSroy 
733ee74c9aSroy 	switch(domain) {
743ee74c9aSroy #ifdef INET
753ee74c9aSroy 	case PF_INET:
763ee74c9aSroy 		s = ctx->pf_inet_fd;
773ee74c9aSroy 		break;
783ee74c9aSroy #endif
793ee74c9aSroy #ifdef INET6
803ee74c9aSroy 	case PF_INET6:
813ee74c9aSroy 		s = priv->pf_inet6_fd;
823ee74c9aSroy 		break;
833ee74c9aSroy #endif
843ee74c9aSroy #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
853ee74c9aSroy 	case PF_LINK:
863ee74c9aSroy 		s = priv->pf_link_fd;
873ee74c9aSroy 		break;
883ee74c9aSroy #endif
893ee74c9aSroy 	default:
903ee74c9aSroy 		errno = EPFNOSUPPORT;
913ee74c9aSroy 		return -1;
923ee74c9aSroy 	}
9322f7cecaSroy 
9454b96bebSroy 	/* Only allow these ioctls */
9554b96bebSroy 	switch(req) {
96c73d34bfSroy #ifdef SIOCGIFDATA
97c73d34bfSroy 	case SIOCGIFDATA:	/* FALLTHROUGH */
98c73d34bfSroy #endif
996006de69Sroy #ifdef SIOCG80211NWID
1006006de69Sroy 	case SIOCG80211NWID:	/* FALLTHROUGH */
1016006de69Sroy #endif
1026006de69Sroy #ifdef SIOCGETVLAN
1036006de69Sroy 	case SIOCGETVLAN:	/* FALLTHROUGH */
1046006de69Sroy #endif
10554b96bebSroy #ifdef SIOCIFAFATTACH
10654b96bebSroy 	case SIOCIFAFATTACH:	/* FALLTHROUGH */
10754b96bebSroy #endif
10854b96bebSroy #ifdef SIOCSIFXFLAGS
10954b96bebSroy 	case SIOCSIFXFLAGS:	/* FALLTHROUGH */
11054b96bebSroy #endif
11154b96bebSroy #ifdef SIOCSIFINFO_FLAGS
11254b96bebSroy 	case SIOCSIFINFO_FLAGS:	/* FALLTHROUGH */
11354b96bebSroy #endif
11454b96bebSroy #ifdef SIOCSRTRFLUSH_IN6
11554b96bebSroy 	case SIOCSRTRFLUSH_IN6:	/* FALLTHROUGH */
11654b96bebSroy 	case SIOCSPFXFLUSH_IN6: /* FALLTHROUGH */
11754b96bebSroy #endif
11854b96bebSroy #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE)
11954b96bebSroy 	case SIOCALIFADDR:	/* FALLTHROUGH */
12054b96bebSroy 	case SIOCDLIFADDR:	/* FALLTHROUGH */
12154b96bebSroy #else
12254b96bebSroy 	case SIOCSIFLLADDR:	/* FALLTHROUGH */
12354b96bebSroy #endif
12454b96bebSroy #ifdef SIOCSIFINFO_IN6
12554b96bebSroy 	case SIOCSIFINFO_IN6:	/* FALLTHROUGH */
12654b96bebSroy #endif
12754b96bebSroy 	case SIOCAIFADDR_IN6:	/* FALLTHROUGH */
1286006de69Sroy 	case SIOCDIFADDR_IN6:
12954b96bebSroy 		break;
13054b96bebSroy 	default:
13154b96bebSroy 		errno = EPERM;
13254b96bebSroy 		return -1;
13354b96bebSroy 	}
13454b96bebSroy 
1353ee74c9aSroy 	return ioctl(s, req, data, len);
13622f7cecaSroy }
13722f7cecaSroy 
13822f7cecaSroy static ssize_t
ps_root_doroute(struct dhcpcd_ctx * ctx,void * data,size_t len)1393ee74c9aSroy ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)
14022f7cecaSroy {
14122f7cecaSroy 
1423ee74c9aSroy 	return write(ctx->link_fd, data, len);
14322f7cecaSroy }
14422f7cecaSroy 
145c73d34bfSroy #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
14654b96bebSroy static ssize_t
ps_root_doindirectioctl(struct dhcpcd_ctx * ctx,unsigned long req,void * data,size_t len)1473ee74c9aSroy ps_root_doindirectioctl(struct dhcpcd_ctx *ctx,
1483ee74c9aSroy     unsigned long req, void *data, size_t len)
14954b96bebSroy {
15054b96bebSroy 	char *p = data;
15154b96bebSroy 	struct ifreq ifr = { .ifr_flags = 0 };
15254b96bebSroy 
1536006de69Sroy 	/* ioctl filtering is done in ps_root_doioctldom */
15454b96bebSroy 
1556006de69Sroy 	if (len < IFNAMSIZ + 1) {
15654b96bebSroy 		errno = EINVAL;
15754b96bebSroy 		return -1;
15854b96bebSroy 	}
15954b96bebSroy 
16054b96bebSroy 	strlcpy(ifr.ifr_name, p, IFNAMSIZ);
1616006de69Sroy 	len -= IFNAMSIZ;
1626006de69Sroy 	memmove(data, p + IFNAMSIZ, len);
1636006de69Sroy 	ifr.ifr_data = data;
1646006de69Sroy 
1653ee74c9aSroy 	return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));
16654b96bebSroy }
167c73d34bfSroy #endif
168708ac11bSroy 
169c73d34bfSroy #ifdef HAVE_PLEDGE
170708ac11bSroy static ssize_t
ps_root_doifignoregroup(struct dhcpcd_ctx * ctx,void * data,size_t len)1713ee74c9aSroy ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)
172708ac11bSroy {
173708ac11bSroy 
174708ac11bSroy 	if (len == 0 || ((const char *)data)[len - 1] != '\0') {
175708ac11bSroy 		errno = EINVAL;
176708ac11bSroy 		return -1;
177708ac11bSroy 	}
178708ac11bSroy 
1793ee74c9aSroy 	return if_ignoregroup(ctx->pf_inet_fd, data);
1803ee74c9aSroy }
1813ee74c9aSroy #endif
1823ee74c9aSroy 
1833ee74c9aSroy #ifdef HAVE_CAPSICUM
1843ee74c9aSroy static ssize_t
ps_root_dosysctl(unsigned long flags,void * data,size_t len,void ** rdata,size_t * rlen)1853ee74c9aSroy ps_root_dosysctl(unsigned long flags,
1863ee74c9aSroy     void *data, size_t len, void **rdata, size_t *rlen)
1873ee74c9aSroy {
1883ee74c9aSroy 	char *p = data, *e = p + len;
1893ee74c9aSroy 	int name[10];
1903ee74c9aSroy 	unsigned int namelen;
1913ee74c9aSroy 	void *oldp;
1923ee74c9aSroy 	size_t *oldlenp, oldlen, nlen;
1933ee74c9aSroy 	void *newp;
1943ee74c9aSroy 	size_t newlen;
1953ee74c9aSroy 	int err;
1963ee74c9aSroy 
1973ee74c9aSroy 	if (sizeof(namelen) >= len) {
1983ee74c9aSroy 		errno = EINVAL;
199708ac11bSroy 		return -1;
2003ee74c9aSroy 	}
2013ee74c9aSroy 	memcpy(&namelen, p, sizeof(namelen));
2023ee74c9aSroy 	p += sizeof(namelen);
2033ee74c9aSroy 	nlen = sizeof(*name) * namelen;
2043ee74c9aSroy 	if (namelen > __arraycount(name)) {
2053ee74c9aSroy 		errno = ENOBUFS;
2063ee74c9aSroy 		return -1;
2073ee74c9aSroy 	}
2083ee74c9aSroy 	if (p + nlen > e) {
2093ee74c9aSroy 		errno = EINVAL;
2103ee74c9aSroy 		return -1;
2113ee74c9aSroy 	}
2123ee74c9aSroy 	memcpy(name, p, nlen);
2133ee74c9aSroy 	p += nlen;
2143ee74c9aSroy 	if (p + sizeof(oldlen) > e) {
2153ee74c9aSroy 		errno = EINVAL;
2163ee74c9aSroy 		return -1;
2173ee74c9aSroy 	}
2183ee74c9aSroy 	memcpy(&oldlen, p, sizeof(oldlen));
2193ee74c9aSroy 	p += sizeof(oldlen);
2203ee74c9aSroy 	if (p + sizeof(newlen) > e) {
2213ee74c9aSroy 		errno = EINVAL;
2223ee74c9aSroy 		return -1;
2233ee74c9aSroy 	}
2243ee74c9aSroy 	memcpy(&newlen, p, sizeof(newlen));
2253ee74c9aSroy 	p += sizeof(newlen);
2263ee74c9aSroy 	if (p + newlen > e) {
2273ee74c9aSroy 		errno = EINVAL;
2283ee74c9aSroy 		return -1;
2293ee74c9aSroy 	}
2303ee74c9aSroy 	newp = newlen ? p : NULL;
2313ee74c9aSroy 
2323ee74c9aSroy 	if (flags & PS_SYSCTL_OLEN) {
2333ee74c9aSroy 		*rlen = sizeof(oldlen) + oldlen;
2343ee74c9aSroy 		*rdata = malloc(*rlen);
2353ee74c9aSroy 		if (*rdata == NULL)
2363ee74c9aSroy 			return -1;
2373ee74c9aSroy 		oldlenp = (size_t *)*rdata;
2383ee74c9aSroy 		*oldlenp = oldlen;
2393ee74c9aSroy 		if (flags & PS_SYSCTL_ODATA)
2403ee74c9aSroy 			oldp = (char *)*rdata + sizeof(oldlen);
2413ee74c9aSroy 		else
2423ee74c9aSroy 			oldp = NULL;
2433ee74c9aSroy 	} else {
2443ee74c9aSroy 		oldlenp = NULL;
2453ee74c9aSroy 		oldp = NULL;
2463ee74c9aSroy 	}
2473ee74c9aSroy 
2483ee74c9aSroy 	err = sysctl(name, namelen, oldp, oldlenp, newp, newlen);
249708ac11bSroy 	return err;
250708ac11bSroy }
25154b96bebSroy #endif
25254b96bebSroy 
25322f7cecaSroy ssize_t
ps_root_os(struct dhcpcd_ctx * ctx,struct ps_msghdr * psm,struct msghdr * msg,void ** rdata,size_t * rlen,bool * free_rdata)2543ee74c9aSroy ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
2553ee74c9aSroy     void **rdata, size_t *rlen, bool *free_rdata)
25622f7cecaSroy {
25722f7cecaSroy 	struct iovec *iov = msg->msg_iov;
25822f7cecaSroy 	void *data = iov->iov_base;
25922f7cecaSroy 	size_t len = iov->iov_len;
2606006de69Sroy 	ssize_t err;
26122f7cecaSroy 
26222f7cecaSroy 	switch (psm->ps_cmd) {
26322f7cecaSroy 	case PS_IOCTLLINK:
2643ee74c9aSroy 		err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len);
2656006de69Sroy 		break;
26622f7cecaSroy 	case PS_IOCTL6:
2673ee74c9aSroy 		err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len);
2686006de69Sroy 		break;
26922f7cecaSroy 	case PS_ROUTE:
2703ee74c9aSroy 		return ps_root_doroute(ctx, data, len);
271c73d34bfSroy #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
27254b96bebSroy 	case PS_IOCTLINDIRECT:
2733ee74c9aSroy 		err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);
2746006de69Sroy 		break;
275c73d34bfSroy #endif
276c73d34bfSroy #ifdef HAVE_PLEDGE
277708ac11bSroy 	case PS_IFIGNOREGRP:
2783ee74c9aSroy 		return ps_root_doifignoregroup(ctx, data, len);
2793ee74c9aSroy #endif
2803ee74c9aSroy #ifdef HAVE_CAPSICUM
2813ee74c9aSroy 	case PS_SYSCTL:
2823ee74c9aSroy 		*free_rdata = true;
2833ee74c9aSroy 		return ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen);
2843ee74c9aSroy #else
2853ee74c9aSroy 	UNUSED(free_rdata);
28654b96bebSroy #endif
28722f7cecaSroy 	default:
28822f7cecaSroy 		errno = ENOTSUP;
28922f7cecaSroy 		return -1;
29022f7cecaSroy 	}
2916006de69Sroy 
2926006de69Sroy 	if (err != -1) {
2936006de69Sroy 		*rdata = data;
2946006de69Sroy 		*rlen = len;
2956006de69Sroy 	}
2966006de69Sroy 	return err;
29722f7cecaSroy }
29822f7cecaSroy 
29922f7cecaSroy static ssize_t
ps_root_ioctldom(struct dhcpcd_ctx * ctx,uint16_t domain,unsigned long request,void * data,size_t len)30054b96bebSroy ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,
30122f7cecaSroy     void *data, size_t len)
30222f7cecaSroy {
30322f7cecaSroy 
304*5ccf87deSroy 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), domain,
30522f7cecaSroy 	    request, data, len) == -1)
30622f7cecaSroy 		return -1;
30754b96bebSroy 	return ps_root_readerror(ctx, data, len);
30822f7cecaSroy }
30922f7cecaSroy 
31022f7cecaSroy ssize_t
ps_root_ioctllink(struct dhcpcd_ctx * ctx,unsigned long request,void * data,size_t len)31122f7cecaSroy ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request,
31222f7cecaSroy     void *data, size_t len)
31322f7cecaSroy {
31422f7cecaSroy 
31522f7cecaSroy 	return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);
31622f7cecaSroy }
31722f7cecaSroy 
31822f7cecaSroy ssize_t
ps_root_ioctl6(struct dhcpcd_ctx * ctx,unsigned long request,void * data,size_t len)31922f7cecaSroy ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request,
32022f7cecaSroy     void *data, size_t len)
32122f7cecaSroy {
32222f7cecaSroy 
32322f7cecaSroy 	return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);
32422f7cecaSroy }
32522f7cecaSroy 
32622f7cecaSroy ssize_t
ps_root_route(struct dhcpcd_ctx * ctx,void * data,size_t len)32722f7cecaSroy ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
32822f7cecaSroy {
32922f7cecaSroy 
330*5ccf87deSroy 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)
33122f7cecaSroy 		return -1;
33254b96bebSroy 	return ps_root_readerror(ctx, data, len);
33322f7cecaSroy }
33454b96bebSroy 
335c73d34bfSroy #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
33654b96bebSroy ssize_t
ps_root_indirectioctl(struct dhcpcd_ctx * ctx,unsigned long request,const char * ifname,void * data,size_t len)33754b96bebSroy ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,
33854b96bebSroy     const char *ifname, void *data, size_t len)
33954b96bebSroy {
34054b96bebSroy 	char buf[PS_BUFLEN];
34154b96bebSroy 
3426006de69Sroy 	if (IFNAMSIZ + len > sizeof(buf)) {
3436006de69Sroy 		errno = ENOBUFS;
3446006de69Sroy 		return -1;
3456006de69Sroy 	}
3466006de69Sroy 
34754b96bebSroy 	strlcpy(buf, ifname, IFNAMSIZ);
34854b96bebSroy 	memcpy(buf + IFNAMSIZ, data, len);
349*5ccf87deSroy 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTLINDIRECT,
35054b96bebSroy 	    request, buf, IFNAMSIZ + len) == -1)
35154b96bebSroy 		return -1;
35254b96bebSroy 	return ps_root_readerror(ctx, data, len);
35354b96bebSroy }
3543ee74c9aSroy #endif
355708ac11bSroy 
3563ee74c9aSroy #ifdef HAVE_PLEDGE
357708ac11bSroy ssize_t
ps_root_ifignoregroup(struct dhcpcd_ctx * ctx,const char * ifname)358708ac11bSroy ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname)
359708ac11bSroy {
360708ac11bSroy 
361*5ccf87deSroy 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IFIGNOREGRP, 0,
362708ac11bSroy 	    ifname, strlen(ifname) + 1) == -1)
363708ac11bSroy 		return -1;
364708ac11bSroy 	return ps_root_readerror(ctx, NULL, 0);
365708ac11bSroy }
36654b96bebSroy #endif
3673ee74c9aSroy 
3683ee74c9aSroy #ifdef HAVE_CAPSICUM
3693ee74c9aSroy ssize_t
ps_root_sysctl(struct dhcpcd_ctx * ctx,const int * name,unsigned int namelen,void * oldp,size_t * oldlenp,const void * newp,size_t newlen)3703ee74c9aSroy ps_root_sysctl(struct dhcpcd_ctx *ctx,
3713ee74c9aSroy     const int *name, unsigned int namelen,
3723ee74c9aSroy     void *oldp, size_t *oldlenp, const void *newp, size_t newlen)
3733ee74c9aSroy {
3743ee74c9aSroy 	char buf[PS_BUFLEN], *p = buf;
3753ee74c9aSroy 	unsigned long flags = 0;
3763ee74c9aSroy 	size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen;
3773ee74c9aSroy 
3783ee74c9aSroy 	if (sizeof(namelen) + (sizeof(*name) * namelen) +
3793ee74c9aSroy 	    sizeof(oldlenp) +
3803ee74c9aSroy 	    sizeof(newlen) + newlen > sizeof(buf))
3813ee74c9aSroy 	{
3823ee74c9aSroy 		errno = ENOBUFS;
3833ee74c9aSroy 		return -1;
3843ee74c9aSroy 	}
3853ee74c9aSroy 
3863ee74c9aSroy 	if (oldlenp)
3873ee74c9aSroy 		flags |= PS_SYSCTL_OLEN;
3883ee74c9aSroy 	if (oldp)
3893ee74c9aSroy 		flags |= PS_SYSCTL_ODATA;
3903ee74c9aSroy 	memcpy(p, &namelen, sizeof(namelen));
3913ee74c9aSroy 	p += sizeof(namelen);
3923ee74c9aSroy 	memcpy(p, name, sizeof(*name) * namelen);
3933ee74c9aSroy 	p += sizeof(*name) * namelen;
3943ee74c9aSroy 	memcpy(p, &olen, sizeof(olen));
3953ee74c9aSroy 	p += sizeof(olen);
3963ee74c9aSroy 	memcpy(p, &newlen, sizeof(newlen));
3973ee74c9aSroy 	p += sizeof(newlen);
3983ee74c9aSroy 	if (newlen) {
3993ee74c9aSroy 		memcpy(p, newp, newlen);
4003ee74c9aSroy 		p += newlen;
4013ee74c9aSroy 	}
4023ee74c9aSroy 
403*5ccf87deSroy 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL,
4043ee74c9aSroy 	    flags, buf, (size_t)(p - buf)) == -1)
4053ee74c9aSroy 		return -1;
4063ee74c9aSroy 
4073ee74c9aSroy 	if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1)
4083ee74c9aSroy 		return -1;
4093ee74c9aSroy 
4103ee74c9aSroy 	p = buf;
4113ee74c9aSroy 	memcpy(&nolen, p, sizeof(nolen));
4123ee74c9aSroy 	p += sizeof(nolen);
4133ee74c9aSroy 	if (oldlenp) {
4143ee74c9aSroy 		*oldlenp = nolen;
4153ee74c9aSroy 		if (oldp && nolen <= olen)
4163ee74c9aSroy 			memcpy(oldp, p, nolen);
4173ee74c9aSroy 	}
4183ee74c9aSroy 
4193ee74c9aSroy 	return 0;
4203ee74c9aSroy }
4213ee74c9aSroy #endif
422