smb: fix memory leak on early failure

... by making sure connection related data (->share) is stored in the
connection and not in the easy handle.

Detected by OSS-fuzz
Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=9369
Fixes #2769
Closes #2810
This commit is contained in:
Daniel Stenberg 2018-07-29 17:58:10 +02:00
Родитель fe60cbfbbf
Коммит 09e401e01b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5CC908FDB71E12C2
2 изменённых файлов: 37 добавлений и 33 удалений

Просмотреть файл

@ -59,6 +59,7 @@
static CURLcode smb_setup_connection(struct connectdata *conn);
static CURLcode smb_connect(struct connectdata *conn, bool *done);
static CURLcode smb_connection_state(struct connectdata *conn, bool *done);
static CURLcode smb_do(struct connectdata *conn, bool *done);
static CURLcode smb_request_state(struct connectdata *conn, bool *done);
static CURLcode smb_done(struct connectdata *conn, CURLcode status,
bool premature);
@ -73,7 +74,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn);
const struct Curl_handler Curl_handler_smb = {
"SMB", /* scheme */
smb_setup_connection, /* setup_connection */
ZERO_NULL, /* do_it */
smb_do, /* do_it */
smb_done, /* done */
ZERO_NULL, /* do_more */
smb_connect, /* connect_it */
@ -98,7 +99,7 @@ const struct Curl_handler Curl_handler_smb = {
const struct Curl_handler Curl_handler_smbs = {
"SMBS", /* scheme */
smb_setup_connection, /* setup_connection */
ZERO_NULL, /* do_it */
smb_do, /* do_it */
smb_done, /* done */
ZERO_NULL, /* do_more */
smb_connect, /* connect_it */
@ -173,7 +174,6 @@ enum smb_req_state {
/* SMB request data */
struct smb_request {
enum smb_req_state state;
char *share;
char *path;
unsigned short tid; /* Even if we connect to the same tree as another */
unsigned short fid; /* request, the tid will be different */
@ -182,7 +182,7 @@ struct smb_request {
static void conn_state(struct connectdata *conn, enum smb_conn_state newstate)
{
struct smb_conn *smb = &conn->proto.smbc;
struct smb_conn *smbc = &conn->proto.smbc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* For debug purposes */
static const char * const names[] = {
@ -194,12 +194,12 @@ static void conn_state(struct connectdata *conn, enum smb_conn_state newstate)
/* LAST */
};
if(smb->state != newstate)
if(smbc->state != newstate)
infof(conn->data, "SMB conn %p state change from %s to %s\n",
(void *)smb, names[smb->state], names[newstate]);
(void *)smbc, names[smbc->state], names[newstate]);
#endif
smb->state = newstate;
smbc->state = newstate;
}
static void request_state(struct connectdata *conn,
@ -228,6 +228,8 @@ static void request_state(struct connectdata *conn,
req->state = newstate;
}
/* this should setup things in the connection, not in the easy
handle */
static CURLcode smb_setup_connection(struct connectdata *conn)
{
struct smb_request *req;
@ -253,7 +255,6 @@ static CURLcode smb_connect(struct connectdata *conn, bool *done)
return CURLE_LOGIN_DENIED;
/* Initialize the connection state */
memset(smbc, 0, sizeof(*smbc));
smbc->state = SMB_CONNECTING;
smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
if(!smbc->recv_buf)
@ -475,11 +476,11 @@ static CURLcode smb_send_setup(struct connectdata *conn)
static CURLcode smb_send_tree_connect(struct connectdata *conn)
{
struct smb_request *req = conn->data->req.protop;
struct smb_tree_connect msg;
struct smb_conn *smbc = &conn->proto.smbc;
char *p = msg.bytes;
size_t byte_count = strlen(conn->host.name) + strlen(req->share);
size_t byte_count = strlen(conn->host.name) + strlen(smbc->share);
byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
if(byte_count > sizeof(msg.bytes))
return CURLE_FILESIZE_EXCEEDED;
@ -491,7 +492,7 @@ static CURLcode smb_send_tree_connect(struct connectdata *conn)
MSGCAT("\\\\");
MSGCAT(conn->host.name);
MSGCAT("\\");
MSGCATNULL(req->share);
MSGCATNULL(smbc->share);
MSGCATNULL(SERVICENAME); /* Match any type of service */
byte_count = p - msg.bytes;
msg.byte_count = smb_swap16((unsigned short)byte_count);
@ -910,31 +911,18 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done)
static CURLcode smb_done(struct connectdata *conn, CURLcode status,
bool premature)
{
struct smb_request *req = conn->data->req.protop;
(void) premature;
Curl_safefree(req->share);
Curl_safefree(conn->data->req.protop);
return status;
}
static CURLcode smb_disconnect(struct connectdata *conn, bool dead)
{
struct smb_conn *smbc = &conn->proto.smbc;
struct smb_request *req = conn->data->req.protop;
(void) dead;
Curl_safefree(smbc->share);
Curl_safefree(smbc->domain);
Curl_safefree(smbc->recv_buf);
/* smb_done is not always called, so cleanup the request */
if(req) {
Curl_safefree(req->share);
}
return CURLE_OK;
}
@ -948,11 +936,27 @@ static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
}
static CURLcode smb_do(struct connectdata *conn, bool *done)
{
struct smb_conn *smbc = &conn->proto.smbc;
struct smb_request *req = conn->data->req.protop;
if(smbc->share) {
req->path = strchr(smbc->share, '\0');
if(req->path) {
req->path++;
*done = TRUE;
return CURLE_OK;
}
}
return CURLE_URL_MALFORMAT;
}
static CURLcode smb_parse_url_path(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct Curl_easy *data = conn->data;
struct smb_request *req = data->req.protop;
struct smb_conn *smbc = &conn->proto.smbc;
char *path;
char *slash;
@ -962,30 +966,29 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
return result;
/* Parse the path for the share */
req->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
free(path);
if(!req->share)
if(!smbc->share)
return CURLE_OUT_OF_MEMORY;
slash = strchr(req->share, '/');
slash = strchr(smbc->share, '/');
if(!slash)
slash = strchr(req->share, '\\');
slash = strchr(smbc->share, '\\');
/* The share must be present */
if(!slash) {
Curl_safefree(req->share);
Curl_safefree(smbc->share);
return CURLE_URL_MALFORMAT;
}
/* Parse the path for the file path converting any forward slashes into
backslashes */
*slash++ = 0;
req->path = slash;
for(; *slash; slash++) {
if(*slash == '/')
*slash = '\\';
}
return CURLE_OK;
}

Просмотреть файл

@ -35,6 +35,7 @@ struct smb_conn {
enum smb_conn_state state;
char *user;
char *domain;
char *share;
unsigned char challenge[8];
unsigned int session_key;
unsigned short uid;