Commit message: Avoid a crash (double-free) when SSH connection fails
CVE: CVE-2017-18594
CWE: CWE-415
Vulnerability description: nse_libssh2.cc in Nmap 7.70 is subject to a denial of service condition due to a double free when an SSH connection fails, as demonstrated by a leading \n character to ssh-brute.nse or ssh-auth-methods.nse.

Project: nmap
File: nse_libssh2.cc

static int finish_session_open (lua_State *L, int status, lua_KContext ctx) {
    assert(lua_gettop(L) == 6);
    if (lua_toboolean(L, -2)) {
        lua_pop(L, 2);
        return do_session_handshake(L,0,0);
    }
    else {
        struct ssh_userdata *state = NULL;

        state = (struct ssh_userdata *) nseU_checkudata(L, 3, SSH2_UDATA, "ssh2");
        if (state->session != NULL) {
            libssh2_session_free(state->session);
            state->session = NULL;
        }
        return lua_error(L);
    }
}

Project: nmap
File: nse_libssh2.cc

static int l_channel_write (lua_State *L) {
    return channel_write(L, 0, 0);
}

Project: nmap

int make_socketpair (SOCKET socks[2], int make_overlapped) {
    union {
        struct sockaddr_in inaddr;
        struct sockaddr addr;
    } a;
    SOCKET listener;
    int e;
    socklen_t addrlen = sizeof(a.inaddr);
    DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0);
    int reuse = 1;

    if (socks == 0) {
        WSASetLastError(WSAEINVAL);
        return SOCKET_ERROR;
    }
    socks[0] = socks[1] = -1;

    listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listener == -1)
        return SOCKET_ERROR;

    memset(&a, 0, sizeof(a));
    a.inaddr.sin_family = AF_INET;
    a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    a.inaddr.sin_port = 0;

    for (;;) {
        if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
            (char*)&reuse, (socklen_t) sizeof(reuse)) == -1)
            break;
        if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
            break;

        memset(&a, 0, sizeof(a));
        if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
            break;
        a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        a.inaddr.sin_family = AF_INET;

        if (listen(listener, 1) == SOCKET_ERROR)
            break;

        socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags);
        if (socks[0] == -1)
            break;
        if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
            break;

        socks[1] = accept(listener, NULL, NULL);
        if (socks[1] == -1)
            break;

        closesocket(listener);
        return 0;
    }

    e = WSAGetLastError();
    closesocket(listener);
    closesocket(socks[0]);
    closesocket(socks[1]);
    WSASetLastError(e);
    socks[0] = socks[1] = -1;

    return -1;
}

Project: nmap
File: nse_libssh2.cc

static int session_close (lua_State *L, int status, lua_KContext ctx) {
    int rc;
    struct ssh_userdata *state;

    state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");

    if (state->session != NULL) {
        while ((rc = libssh2_session_disconnect(
            state->session, "Normal Shutdown")) == LIBSSH2_ERROR_EAGAIN) {
            luaL_getmetafield(L, 1, "filter");
            lua_pushvalue(L, 1);

            assert(lua_status(L) == LUA_OK);
            lua_callk(L, 1, 0, 0, session_close);
        }

        if (rc < 0)
            return luaL_error(L, "unable to disconnect session");

        if (libssh2_session_free(state->session) < 0)
            return luaL_error(L, "unable to free session");

        state->session = NULL;
    }

    return 0;
}