Unverified Commit ce8803a2 by Patrick Steinhardt Committed by GitHub

Merge pull request #4836 from pks-t/pks/smart-packets

Smart packet security fixes
parents 84d6f439 1bc5b05c
...@@ -90,7 +90,7 @@ typedef struct { ...@@ -90,7 +90,7 @@ typedef struct {
typedef struct { typedef struct {
git_pkt_type type; git_pkt_type type;
int len; size_t len;
char data[GIT_FLEX_ARRAY]; char data[GIT_FLEX_ARRAY];
} git_pkt_data; } git_pkt_data;
...@@ -98,7 +98,7 @@ typedef git_pkt_data git_pkt_progress; ...@@ -98,7 +98,7 @@ typedef git_pkt_data git_pkt_progress;
typedef struct { typedef struct {
git_pkt_type type; git_pkt_type type;
int len; size_t len;
char error[GIT_FLEX_ARRAY]; char error[GIT_FLEX_ARRAY];
} git_pkt_err; } git_pkt_err;
...@@ -188,7 +188,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream ...@@ -188,7 +188,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream
int git_smart__update_heads(transport_smart *t, git_vector *symrefs); int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */ /* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen);
int git_pkt_buffer_flush(git_buf *buf); int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf); int git_pkt_buffer_done(git_buf *buf);
......
...@@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out) ...@@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out)
static int ack_pkt(git_pkt **out, const char *line, size_t len) static int ack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ack *pkt; git_pkt_ack *pkt;
GIT_UNUSED(line);
GIT_UNUSED(len);
pkt = git__calloc(1, sizeof(git_pkt_ack)); pkt = git__calloc(1, sizeof(git_pkt_ack));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ACK; pkt->type = GIT_PKT_ACK;
line += 3;
len -= 3;
if (len >= GIT_OID_HEXSZ) { if (git__prefixncmp(line, len, "ACK "))
git_oid_fromstr(&pkt->oid, line + 1); goto out_err;
line += GIT_OID_HEXSZ + 1; line += 4;
len -= GIT_OID_HEXSZ + 1; len -= 4;
}
if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0)
goto out_err;
line += GIT_OID_HEXSZ;
len -= GIT_OID_HEXSZ;
if (len && line[0] == ' ') {
line++;
len--;
if (len >= 7) { if (!git__prefixncmp(line, len, "continue"))
if (!git__prefixcmp(line + 1, "continue"))
pkt->status = GIT_ACK_CONTINUE; pkt->status = GIT_ACK_CONTINUE;
if (!git__prefixcmp(line + 1, "common")) else if (!git__prefixncmp(line, len, "common"))
pkt->status = GIT_ACK_COMMON; pkt->status = GIT_ACK_COMMON;
if (!git__prefixcmp(line + 1, "ready")) else if (!git__prefixncmp(line, len, "ready"))
pkt->status = GIT_ACK_READY; pkt->status = GIT_ACK_READY;
else
goto out_err;
} }
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ACK pkt-line");
git__free(pkt);
return -1;
} }
static int nak_pkt(git_pkt **out) static int nak_pkt(git_pkt **out)
...@@ -107,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len) ...@@ -107,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
static int err_pkt(git_pkt **out, const char *line, size_t len) static int err_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_err *pkt; git_pkt_err *pkt = NULL;
size_t alloclen; size_t alloclen;
/* Remove "ERR " from the line */ /* Remove "ERR " from the line */
if (git__prefixncmp(line, len, "ERR "))
goto out_err;
line += 4; line += 4;
len -= 4; len -= 4;
...@@ -118,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len) ...@@ -118,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
pkt = git__malloc(alloclen); pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR; pkt->type = GIT_PKT_ERR;
pkt->len = (int)len; pkt->len = len;
memcpy(pkt->error, line, len); memcpy(pkt->error, line, len);
pkt->error[len] = '\0'; pkt->error[len] = '\0';
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing ERR pkt-line");
git__free(pkt);
return -1;
} }
static int data_pkt(git_pkt **out, const char *line, size_t len) static int data_pkt(git_pkt **out, const char *line, size_t len)
...@@ -142,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len) ...@@ -142,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_DATA; pkt->type = GIT_PKT_DATA;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
...@@ -163,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) ...@@ -163,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PROGRESS; pkt->type = GIT_PKT_PROGRESS;
pkt->len = (int) len; pkt->len = len;
memcpy(pkt->data, line, len); memcpy(pkt->data, line, len);
*out = (git_pkt *) pkt; *out = (git_pkt *) pkt;
...@@ -199,33 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) ...@@ -199,33 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
*/ */
static int ref_pkt(git_pkt **out, const char *line, size_t len) static int ref_pkt(git_pkt **out, const char *line, size_t len)
{ {
int error;
git_pkt_ref *pkt; git_pkt_ref *pkt;
size_t alloclen; size_t alloclen;
if (len < GIT_OID_HEXSZ + 1) { pkt = git__calloc(1, sizeof(git_pkt_ref));
giterr_set(GITERR_NET, "error parsing pkt-line");
return -1;
}
pkt = git__malloc(sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF; pkt->type = GIT_PKT_REF;
if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
goto error_out;
/* Check for a bit of consistency */
if (line[GIT_OID_HEXSZ] != ' ') {
giterr_set(GITERR_NET, "error parsing pkt-line");
error = -1;
goto error_out;
}
/* Jump from the name */ if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0)
line += GIT_OID_HEXSZ + 1; goto out_err;
len -= (GIT_OID_HEXSZ + 1); line += GIT_OID_HEXSZ;
len -= GIT_OID_HEXSZ;
if (git__prefixncmp(line, len, " "))
goto out_err;
line++;
len--;
if (!len)
goto out_err;
if (line[len - 1] == '\n') if (line[len - 1] == '\n')
--len; --len;
...@@ -237,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) ...@@ -237,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->head.name, line, len); memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0'; pkt->head.name[len] = '\0';
if (strlen(pkt->head.name) < len) { if (strlen(pkt->head.name) < len)
pkt->capabilities = strchr(pkt->head.name, '\0') + 1; pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
error_out: out_err:
giterr_set(GITERR_NET, "error parsing REF pkt-line");
if (pkt)
git__free(pkt->head.name);
git__free(pkt); git__free(pkt);
return error; return -1;
} }
static int ok_pkt(git_pkt **out, const char *line, size_t len) static int ok_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_ok *pkt; git_pkt_ok *pkt;
const char *ptr;
size_t alloc_len; size_t alloc_len;
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_OK; pkt->type = GIT_PKT_OK;
line += 3; /* skip "ok " */ if (git__prefixncmp(line, len, "ok "))
if (!(ptr = strchr(line, '\n'))) { goto out_err;
giterr_set(GITERR_NET, "invalid packet line"); line += 3;
git__free(pkt); len -= 3;
return -1;
} if (line[len - 1] == '\n')
len = ptr - line; --len;
GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
pkt->ref = git__malloc(alloc_len); pkt->ref = git__malloc(alloc_len);
...@@ -277,6 +285,11 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) ...@@ -277,6 +285,11 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len)
*out = (git_pkt *)pkt; *out = (git_pkt *)pkt;
return 0; return 0;
out_err:
giterr_set(GITERR_NET, "error parsing OK pkt-line");
git__free(pkt);
return -1;
} }
static int ng_pkt(git_pkt **out, const char *line, size_t len) static int ng_pkt(git_pkt **out, const char *line, size_t len)
...@@ -293,9 +306,9 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) ...@@ -293,9 +306,9 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
eol = line + len; eol = line + len;
if (len < 3) if (git__prefixncmp(line, len, "ng "))
goto out_err; goto out_err;
line += 3; /* skip "ng " */ line += 3;
if (!(ptr = memchr(line, ' ', eol - line))) if (!(ptr = memchr(line, ' ', eol - line)))
goto out_err; goto out_err;
...@@ -337,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) ...@@ -337,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
{ {
git_pkt_unpack *pkt; git_pkt_unpack *pkt;
GIT_UNUSED(len);
pkt = git__malloc(sizeof(*pkt)); pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt); GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_UNPACK; pkt->type = GIT_PKT_UNPACK;
if (!git__prefixcmp(line, "unpack ok"))
if (!git__prefixncmp(line, len, "unpack ok"))
pkt->unpack_ok = 1; pkt->unpack_ok = 1;
else else
pkt->unpack_ok = 0; pkt->unpack_ok = 0;
...@@ -352,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) ...@@ -352,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
return 0; return 0;
} }
static int32_t parse_len(const char *line) static int parse_len(size_t *out, const char *line, size_t linelen)
{ {
char num[PKT_LEN_SIZE + 1]; char num[PKT_LEN_SIZE + 1];
int i, k, error; int i, k, error;
int32_t len; int32_t len;
const char *num_end; const char *num_end;
/* Not even enough for the length */
if (linelen < PKT_LEN_SIZE)
return GIT_EBUFS;
memcpy(num, line, PKT_LEN_SIZE); memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0'; num[PKT_LEN_SIZE] = '\0';
...@@ -379,7 +394,11 @@ static int32_t parse_len(const char *line) ...@@ -379,7 +394,11 @@ static int32_t parse_len(const char *line)
if ((error = git__strtol32(&len, num, &num_end, 16)) < 0) if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
return error; return error;
return len; if (len < 0)
return -1;
*out = (size_t) len;
return 0;
} }
/* /*
...@@ -396,35 +415,32 @@ static int32_t parse_len(const char *line) ...@@ -396,35 +415,32 @@ static int32_t parse_len(const char *line)
*/ */
int git_pkt_parse_line( int git_pkt_parse_line(
git_pkt **head, const char *line, const char **out, size_t bufflen) git_pkt **pkt, const char **endptr, const char *line, size_t linelen)
{ {
int ret; int error;
int32_t len; size_t len;
/* Not even enough for the length */
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
return GIT_EBUFS;
len = parse_len(line); if ((error = parse_len(&len, line, linelen)) < 0) {
if (len < 0) {
/* /*
* If we fail to parse the length, it might be because the * If we fail to parse the length, it might be
* server is trying to send us the packfile already. * because the server is trying to send us the
* packfile already or because we do not yet have
* enough data.
*/ */
if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { if (error == GIT_EBUFS)
;
else if (!git__prefixncmp(line, linelen, "PACK"))
giterr_set(GITERR_NET, "unexpected pack file"); giterr_set(GITERR_NET, "unexpected pack file");
} else { else
giterr_set(GITERR_NET, "bad packet length"); giterr_set(GITERR_NET, "bad packet length");
} return error;
return -1;
} }
/* /*
* If we were given a buffer length, then make sure there is * Make sure there is enough in the buffer to satisfy
* enough in the buffer to satisfy this line * this line.
*/ */
if (bufflen > 0 && bufflen < (size_t)len) if (linelen < len)
return GIT_EBUFS; return GIT_EBUFS;
/* /*
...@@ -447,38 +463,38 @@ int git_pkt_parse_line( ...@@ -447,38 +463,38 @@ int git_pkt_parse_line(
} }
if (len == 0) { /* Flush pkt */ if (len == 0) { /* Flush pkt */
*out = line; *endptr = line;
return flush_pkt(head); return flush_pkt(pkt);
} }
len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
if (*line == GIT_SIDE_BAND_DATA) if (*line == GIT_SIDE_BAND_DATA)
ret = data_pkt(head, line, len); error = data_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_PROGRESS) else if (*line == GIT_SIDE_BAND_PROGRESS)
ret = sideband_progress_pkt(head, line, len); error = sideband_progress_pkt(pkt, line, len);
else if (*line == GIT_SIDE_BAND_ERROR) else if (*line == GIT_SIDE_BAND_ERROR)
ret = sideband_error_pkt(head, line, len); error = sideband_error_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ACK")) else if (!git__prefixncmp(line, len, "ACK"))
ret = ack_pkt(head, line, len); error = ack_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "NAK")) else if (!git__prefixncmp(line, len, "NAK"))
ret = nak_pkt(head); error = nak_pkt(pkt);
else if (!git__prefixcmp(line, "ERR ")) else if (!git__prefixncmp(line, len, "ERR"))
ret = err_pkt(head, line, len); error = err_pkt(pkt, line, len);
else if (*line == '#') else if (*line == '#')
ret = comment_pkt(head, line, len); error = comment_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ok")) else if (!git__prefixncmp(line, len, "ok"))
ret = ok_pkt(head, line, len); error = ok_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "ng")) else if (!git__prefixncmp(line, len, "ng"))
ret = ng_pkt(head, line, len); error = ng_pkt(pkt, line, len);
else if (!git__prefixcmp(line, "unpack")) else if (!git__prefixncmp(line, len, "unpack"))
ret = unpack_pkt(head, line, len); error = unpack_pkt(pkt, line, len);
else else
ret = ref_pkt(head, line, len); error = ref_pkt(pkt, line, len);
*out = line + len; *endptr = line + len;
return ret; return error;
} }
void git_pkt_free(git_pkt *pkt) void git_pkt_free(git_pkt *pkt)
......
...@@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) ...@@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
...@@ -217,7 +217,7 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf ...@@ -217,7 +217,7 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf
do { do {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
...@@ -755,7 +755,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, ...@@ -755,7 +755,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
} }
while (line_len > 0) { while (line_len > 0) {
error = git_pkt_parse_line(&pkt, line, &line_end, line_len); error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
if (error == GIT_EBUFS) { if (error == GIT_EBUFS) {
/* Buffer the data when the inner packet is split /* Buffer the data when the inner packet is split
...@@ -798,8 +798,8 @@ static int parse_report(transport_smart *transport, git_push *push) ...@@ -798,8 +798,8 @@ static int parse_report(transport_smart *transport, git_push *push)
for (;;) { for (;;) {
if (buf->offset > 0) if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, error = git_pkt_parse_line(&pkt, &line_end,
&line_end, buf->offset); buf->data, buf->offset);
else else
error = GIT_EBUFS; error = GIT_EBUFS;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment