Skip to content

Commit 661cdbd

Browse files
authored
privsep: Ensure we recv for real after a successful recv MSG_PEEK
* privsep: Ensure we recv for real after a successful recv MSG_PEEK Adjust the code flow so that the same errors would be caught after the final recv. This ensures we read what is really meant for us and not something silly. Return EBADMSG on recvmsg len mismatch.
1 parent 2c1db04 commit 661cdbd

File tree

3 files changed

+77
-95
lines changed

3 files changed

+77
-95
lines changed

src/privsep-root.c

Lines changed: 77 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -71,108 +71,119 @@ struct psr_ctx {
7171
struct psr_error psr_error;
7272
size_t psr_datalen;
7373
void *psr_data;
74-
size_t psr_mdatalen;
75-
void *psr_mdata;
76-
bool psr_usemdata;
74+
bool psr_mallocdata;
7775
};
7876

7977
static ssize_t
80-
ps_root_readerrorcb(struct psr_ctx *psr_ctx)
78+
ps_root_readerrorcb(struct psr_ctx *pc)
8179
{
82-
struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
80+
struct dhcpcd_ctx *ctx = pc->psr_ctx;
8381
int fd = PS_ROOT_FD(ctx);
84-
struct psr_error *psr_error = &psr_ctx->psr_error;
82+
struct psr_error *psr_error = &pc->psr_error;
8583
struct iovec iov[] = {
8684
{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
87-
{ .iov_base = NULL, .iov_len = 0 },
85+
{ .iov_base = pc->psr_data, .iov_len = pc->psr_datalen },
8886
};
87+
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
8988
ssize_t len;
9089

9190
#define PSR_ERROR(e) \
9291
do { \
93-
psr_error->psr_result = -1; \
9492
psr_error->psr_errno = (e); \
95-
return -1; \
93+
goto error; \
9694
} while (0 /* CONSTCOND */)
9795

9896
if (eloop_waitfd(fd) == -1)
9997
PSR_ERROR(errno);
10098

101-
len = recv(fd, psr_error, sizeof(*psr_error), MSG_PEEK);
99+
if (!pc->psr_mallocdata)
100+
goto recv;
101+
102+
/* We peek at the psr_error structure to tell us how much of a buffer
103+
* we need to read the whole packet. */
104+
msg.msg_iovlen--;
105+
len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
102106
if (len == -1)
103107
PSR_ERROR(errno);
104-
else if ((size_t)len < sizeof(*psr_error))
105-
PSR_ERROR(EINVAL);
106108

107-
if (psr_error->psr_datalen > SSIZE_MAX)
108-
PSR_ERROR(ENOBUFS);
109-
if (psr_ctx->psr_usemdata &&
110-
psr_error->psr_datalen > psr_ctx->psr_mdatalen)
111-
{
112-
void *d = realloc(psr_ctx->psr_mdata, psr_error->psr_datalen);
113-
if (d == NULL)
114-
PSR_ERROR(errno);
115-
psr_ctx->psr_mdata = d;
116-
psr_ctx->psr_mdatalen = psr_error->psr_datalen;
109+
/* After this point, we MUST do another recvmsg even on a failure
110+
* to remove the message after peeking. */
111+
if ((size_t)len < sizeof(*psr_error)) {
112+
/* We can't use the header to work out buffers, so
113+
* remove the message and bail. */
114+
(void)recvmsg(fd, &msg, MSG_WAITALL);
115+
PSR_ERROR(EINVAL);
117116
}
118-
if (psr_error->psr_datalen != 0) {
119-
if (psr_ctx->psr_usemdata)
120-
iov[1].iov_base = psr_ctx->psr_mdata;
121-
else {
122-
if (psr_error->psr_datalen > psr_ctx->psr_datalen)
123-
PSR_ERROR(ENOBUFS);
124-
iov[1].iov_base = psr_ctx->psr_data;
125-
}
117+
118+
/* No data to read? Unlikely but ... */
119+
if (psr_error->psr_datalen == 0)
120+
goto recv;
121+
122+
pc->psr_data = malloc(psr_error->psr_datalen);
123+
if (pc->psr_data != NULL) {
124+
iov[1].iov_base = pc->psr_data;
126125
iov[1].iov_len = psr_error->psr_datalen;
126+
msg.msg_iovlen++;
127127
}
128128

129-
len = readv(fd, iov, __arraycount(iov));
129+
recv:
130+
len = recvmsg(fd, &msg, MSG_WAITALL);
130131
if (len == -1)
131132
PSR_ERROR(errno);
132-
else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen)
133+
else if ((size_t)len < sizeof(*psr_error))
133134
PSR_ERROR(EINVAL);
135+
else if (msg.msg_flags & MSG_TRUNC)
136+
PSR_ERROR(ENOBUFS);
137+
else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
138+
#ifdef PRIVSEP_DEBUG
139+
logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
140+
len, sizeof(*psr_error) + psr_error->psr_datalen);
141+
#endif
142+
PSR_ERROR(EBADMSG);
143+
}
134144
return len;
145+
146+
error:
147+
psr_error->psr_result = -1;
148+
if (pc->psr_mallocdata && pc->psr_data != NULL) {
149+
free(pc->psr_data);
150+
pc->psr_data = NULL;
151+
}
152+
return -1;
135153
}
136154

137155
ssize_t
138156
ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
139157
{
140-
struct psr_ctx *pc = ctx->ps_root->psp_data;
158+
struct psr_ctx pc = {
159+
.psr_ctx = ctx,
160+
.psr_data = data,
161+
.psr_datalen = len,
162+
.psr_mallocdata = false
163+
};
141164

142-
pc->psr_data = data;
143-
pc->psr_datalen = len;
144-
pc->psr_usemdata = false;
145-
ps_root_readerrorcb(pc);
165+
ps_root_readerrorcb(&pc);
146166

147-
errno = pc->psr_error.psr_errno;
148-
return pc->psr_error.psr_result;
167+
errno = pc.psr_error.psr_errno;
168+
return pc.psr_error.psr_result;
149169
}
150170

151171
ssize_t
152172
ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
153173
{
154-
struct psr_ctx *pc = ctx->ps_root->psp_data;
155-
void *d;
174+
struct psr_ctx pc = {
175+
.psr_ctx = ctx,
176+
.psr_data = NULL,
177+
.psr_datalen = 0,
178+
.psr_mallocdata = true
179+
};
156180

157-
pc->psr_usemdata = true;
158-
ps_root_readerrorcb(pc);
181+
ps_root_readerrorcb(&pc);
159182

160-
if (pc->psr_error.psr_datalen != 0) {
161-
if (pc->psr_error.psr_datalen > pc->psr_mdatalen) {
162-
errno = EINVAL;
163-
return -1;
164-
}
165-
d = malloc(pc->psr_error.psr_datalen);
166-
if (d == NULL)
167-
return -1;
168-
memcpy(d, pc->psr_mdata, pc->psr_error.psr_datalen);
169-
} else
170-
d = NULL;
171-
172-
errno = pc->psr_error.psr_errno;
173-
*data = d;
174-
*len = pc->psr_error.psr_datalen;
175-
return pc->psr_error.psr_result;
183+
errno = pc.psr_error.psr_errno;
184+
*data = pc.psr_data;
185+
*len = pc.psr_error.psr_datalen;
186+
return pc.psr_error.psr_result;
176187
}
177188

178189
static ssize_t
@@ -196,6 +207,8 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
196207
logdebugx("%s: result %zd errno %d", __func__, result, errno);
197208
#endif
198209

210+
if (len == 0)
211+
msg.msg_iovlen = 1;
199212
err = sendmsg(fd, &msg, MSG_EOR);
200213

201214
/* Error sending the message? Try sending the error of sending. */
@@ -204,8 +217,8 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
204217
__func__, result, data, len);
205218
psr.psr_result = err;
206219
psr.psr_errno = errno;
207-
iov[1].iov_base = NULL;
208-
iov[1].iov_len = 0;
220+
psr.psr_datalen = 0;
221+
msg.msg_iovlen = 1;
209222
err = sendmsg(fd, &msg, MSG_EOR);
210223
}
211224

@@ -602,7 +615,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
602615
break;
603616
}
604617

605-
err = ps_root_writeerror(ctx, err, rlen != 0 ? rdata : 0, rlen);
618+
err = ps_root_writeerror(ctx, err, rdata, rlen);
606619
if (free_rdata)
607620
free(rdata);
608621
return err;
@@ -843,17 +856,6 @@ ps_root_log(void *arg, unsigned short events)
843856
logerr(__func__);
844857
}
845858

846-
static void
847-
ps_root_freepsdata(void *arg)
848-
{
849-
struct psr_ctx *pc = arg;
850-
851-
if (pc == NULL)
852-
return;
853-
free(pc->psr_mdata);
854-
free(pc);
855-
}
856-
857859
pid_t
858860
ps_root_start(struct dhcpcd_ctx *ctx)
859861
{
@@ -864,7 +866,6 @@ ps_root_start(struct dhcpcd_ctx *ctx)
864866
struct ps_process *psp;
865867
int logfd[2] = { -1, -1}, datafd[2] = { -1, -1};
866868
pid_t pid;
867-
struct psr_ctx *pc;
868869

869870
if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
870871
return -1;
@@ -883,27 +884,15 @@ ps_root_start(struct dhcpcd_ctx *ctx)
883884
return -1;
884885
#endif
885886

886-
pc = calloc(1, sizeof(*pc));
887-
if (pc == NULL)
888-
return -1;
889-
pc->psr_ctx = ctx;
890-
891887
psp = ctx->ps_root = ps_newprocess(ctx, &id);
892888
if (psp == NULL)
893-
{
894-
free(pc);
895889
return -1;
896-
}
897-
psp->psp_freedata = ps_root_freepsdata;
890+
898891
strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
899892
pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
900893
ps_root_startcb, PSF_ELOOP);
901-
if (pid == -1) {
902-
free(pc);
894+
if (pid == -1)
903895
return -1;
904-
}
905-
906-
psp->psp_data = pc;
907896

908897
if (pid == 0) {
909898
ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */

src/privsep.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,11 +761,6 @@ ps_freeprocess(struct ps_process *psp)
761761

762762
TAILQ_REMOVE(&ctx->ps_processes, psp, next);
763763

764-
if (psp->psp_freedata != NULL)
765-
psp->psp_freedata(psp->psp_data);
766-
else
767-
free(psp->psp_data);
768-
769764
if (psp->psp_fd != -1) {
770765
eloop_event_delete(ctx->eloop, psp->psp_fd);
771766
close(psp->psp_fd);

src/privsep.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,6 @@ struct ps_process {
184184
char psp_name[PSP_NAMESIZE];
185185
uint16_t psp_proto;
186186
const char *psp_protostr;
187-
void *psp_data;
188-
void (*psp_freedata)(void *);
189187
bool psp_started;
190188

191189
#ifdef INET

0 commit comments

Comments
 (0)