mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
keyboard-interactive auth for NSE via libssh2
This commit is contained in:
206
nse_libssh2.cc
206
nse_libssh2.cc
@@ -15,6 +15,7 @@ extern "C" {
|
||||
#include "nse_nsock.h"
|
||||
#include "nse_utility.h"
|
||||
#include "nbase.h"
|
||||
#include "nmap_error.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
@@ -43,17 +44,27 @@ enum {
|
||||
SSH2_UDATA = lua_upvalueindex(1)
|
||||
};
|
||||
|
||||
struct userauth_context {
|
||||
const char *username;
|
||||
size_t username_len;
|
||||
const char *privkey; // or password
|
||||
size_t privkey_len;
|
||||
const char *pubkey;
|
||||
size_t pubkey_len;
|
||||
const char *passphrase;
|
||||
int kbdint_callback_ref;
|
||||
};
|
||||
|
||||
struct ssh_userdata {
|
||||
#ifdef WIN32
|
||||
struct ssh_userdata {
|
||||
SOCKET sp[2];
|
||||
LIBSSH2_SESSION *session;
|
||||
};
|
||||
#else
|
||||
struct ssh_userdata {
|
||||
int sp[2];
|
||||
LIBSSH2_SESSION *session;
|
||||
};
|
||||
#endif
|
||||
LIBSSH2_SESSION *session;
|
||||
lua_State *L;
|
||||
userauth_context userauth;
|
||||
};
|
||||
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
@@ -341,9 +352,10 @@ static int l_session_open (lua_State *L) {
|
||||
state = (ssh_userdata *)lua_newuserdatauv(L, sizeof(ssh_userdata), 1); /* index 3 */
|
||||
|
||||
assert(lua_gettop(L) == 3);
|
||||
state->session = NULL;
|
||||
memset(state, 0, sizeof(ssh_userdata));
|
||||
state->sp[0] = -1;
|
||||
state->sp[1] = -1;
|
||||
state->L = L;
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* metatable */
|
||||
lua_setmetatable(L, 3);
|
||||
|
||||
@@ -352,7 +364,7 @@ static int l_session_open (lua_State *L) {
|
||||
lua_getuservalue(L, 3); /* index 4 - a table associated with userdata*/
|
||||
assert(lua_gettop(L) == 4);
|
||||
|
||||
state->session = libssh2_session_init();
|
||||
state->session = libssh2_session_init_ex(NULL, NULL, NULL, state);
|
||||
|
||||
if (state->session == NULL) {
|
||||
// A session could not be created because of memory limit
|
||||
@@ -504,32 +516,21 @@ static int l_userauth_banner (lua_State *L) {
|
||||
return userauth_banner(L, 0, 0);
|
||||
}
|
||||
|
||||
struct publickey_ctx {
|
||||
struct ssh_userdata *state;
|
||||
const char *username;
|
||||
size_t username_len;
|
||||
const char *privkey;
|
||||
size_t privkey_len;
|
||||
const char *pubkey;
|
||||
size_t pubkey_len;
|
||||
const char *passphrase;
|
||||
};
|
||||
|
||||
static void validate_publickey_params(lua_State *L, struct publickey_ctx *ctx) {
|
||||
memset(ctx, 0, sizeof(struct publickey_ctx));
|
||||
ctx->state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
ctx->username = luaL_checklstring(L, 2, &ctx->username_len);
|
||||
ctx->privkey = luaL_checklstring(L, 3, &ctx->privkey_len);
|
||||
|
||||
ctx->passphrase = lua_isstring(L, 4) ? lua_tostring(L, 4) : NULL;
|
||||
ctx->pubkey = lua_isstring(L, 5) ? lua_tolstring(L, 5, &ctx->pubkey_len) : NULL;
|
||||
static void validate_publickey_params(lua_State *L, ssh_userdata *state, int params_idx) {
|
||||
userauth_context *ctx = &state->userauth;
|
||||
memset(ctx, 0, sizeof(userauth_context));
|
||||
ctx->username = luaL_checklstring(L, params_idx, &ctx->username_len);
|
||||
ctx->privkey = luaL_checklstring(L, params_idx + 1, &ctx->privkey_len);
|
||||
ctx->passphrase = lua_tostring(L, params_idx + 2);
|
||||
ctx->pubkey = lua_tolstring(L, params_idx + 3, &ctx->pubkey_len);
|
||||
}
|
||||
|
||||
static int userauth_publickey (lua_State *L, int status, lua_KContext ctx) {
|
||||
struct publickey_ctx *context = (struct publickey_ctx *)ctx;
|
||||
ssh_userdata *state = (ssh_userdata *)ctx;
|
||||
userauth_context *context = &state->userauth;
|
||||
int rc;
|
||||
DO_OR_YIELD((rc = libssh2_userauth_publickey_fromfile_ex(
|
||||
context->state->session, context->username, context->username_len,
|
||||
state->session, context->username, context->username_len,
|
||||
context->pubkey, context->privkey, context->passphrase
|
||||
)),
|
||||
1, userauth_publickey, ctx);
|
||||
@@ -540,17 +541,17 @@ static int userauth_publickey (lua_State *L, int status, lua_KContext ctx) {
|
||||
}
|
||||
|
||||
static int l_userauth_publickey (lua_State *L) {
|
||||
publickey_ctx *params = NULL;
|
||||
params = (publickey_ctx *)lua_newuserdatauv(L, sizeof(publickey_ctx), 0);
|
||||
validate_publickey_params(L, params);
|
||||
return userauth_publickey(L, 0, (lua_KContext) params);
|
||||
ssh_userdata *state = (ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
validate_publickey_params(L, state, 2);
|
||||
return userauth_publickey(L, 0, (lua_KContext) state);
|
||||
}
|
||||
|
||||
static int userauth_publickey_frommemory (lua_State *L, int status, lua_KContext ctx) {
|
||||
struct publickey_ctx *context = (struct publickey_ctx *)ctx;
|
||||
ssh_userdata *state = (ssh_userdata *)ctx;
|
||||
userauth_context *context = &state->userauth;
|
||||
int rc;
|
||||
DO_OR_YIELD((rc = libssh2_userauth_publickey_frommemory(
|
||||
context->state->session, context->username, context->username_len,
|
||||
state->session, context->username, context->username_len,
|
||||
context->pubkey, context->pubkey_len, context->privkey,
|
||||
context->privkey_len, context->passphrase
|
||||
)),
|
||||
@@ -562,10 +563,9 @@ static int userauth_publickey_frommemory (lua_State *L, int status, lua_KContext
|
||||
}
|
||||
|
||||
static int l_userauth_publickey_frommemory (lua_State *L) {
|
||||
publickey_ctx *params = NULL;
|
||||
params = (publickey_ctx *)lua_newuserdatauv(L, sizeof(publickey_ctx), 0);
|
||||
validate_publickey_params(L, params);
|
||||
return userauth_publickey_frommemory(L, 0, (lua_KContext) params);
|
||||
ssh_userdata *state = (ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
validate_publickey_params(L, state, 2);
|
||||
return userauth_publickey_frommemory(L, 0, (lua_KContext) state);
|
||||
}
|
||||
|
||||
static int l_read_publickey (lua_State *L) {
|
||||
@@ -605,23 +605,13 @@ static int publickey_canauth_cb (LIBSSH2_SESSION *session, unsigned char **sig,
|
||||
}
|
||||
|
||||
static int publickey_canauth (lua_State *L, int status, lua_KContext ctx) {
|
||||
ssh_userdata *state = (ssh_userdata *)ctx;
|
||||
userauth_context *context = &state->userauth;
|
||||
int rc;
|
||||
char *errmsg;
|
||||
const char *username;
|
||||
unsigned const char *publickey_data;
|
||||
size_t len = 0;
|
||||
struct ssh_userdata *state;
|
||||
|
||||
state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
username = luaL_checkstring(L, 2);
|
||||
|
||||
if (lua_isstring(L, 3))
|
||||
publickey_data = (unsigned const char*)lua_tolstring(L, 3, &len);
|
||||
else
|
||||
return luaL_error(L, "Invalid public key");
|
||||
|
||||
DO_OR_YIELD((rc = libssh2_userauth_publickey(state->session,
|
||||
username, publickey_data, len, &publickey_canauth_cb, NULL)),
|
||||
context->username, (const unsigned char *)context->pubkey, context->pubkey_len, &publickey_canauth_cb, NULL)),
|
||||
1, publickey_canauth, ctx);
|
||||
|
||||
libssh2_session_last_error(state->session, &errmsg, NULL, 0);
|
||||
@@ -641,7 +631,12 @@ static int publickey_canauth (lua_State *L, int status, lua_KContext ctx) {
|
||||
}
|
||||
|
||||
static int l_publickey_canauth (lua_State *L) {
|
||||
return publickey_canauth(L, 0, 0);
|
||||
ssh_userdata *state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
userauth_context *context = &state->userauth;
|
||||
context->username = luaL_checklstring(L, 2, &context->username_len);
|
||||
context->pubkey = luaL_checklstring(L, 3, &context->pubkey_len);
|
||||
|
||||
return publickey_canauth(L, 0, (lua_KContext)state);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -651,17 +646,15 @@ static int l_publickey_canauth (lua_State *L) {
|
||||
* userauth_password(state, username, password)
|
||||
*/
|
||||
static int userauth_password (lua_State *L, int status, lua_KContext ctx) {
|
||||
ssh_userdata *state = (ssh_userdata *)ctx;
|
||||
userauth_context *context = &state->userauth;
|
||||
int rc;
|
||||
const char *username, *password;
|
||||
struct ssh_userdata *state;
|
||||
|
||||
state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
username = luaL_checkstring(L, 2);
|
||||
password = luaL_checkstring(L, 3);
|
||||
|
||||
assert(state->session != NULL);
|
||||
DO_OR_YIELD((rc = libssh2_userauth_password(state->session, username, password)),
|
||||
1, userauth_password, ctx);
|
||||
DO_OR_YIELD((rc = libssh2_userauth_password_ex(state->session,
|
||||
context->username, context->username_len,
|
||||
context->privkey, context->privkey_len, NULL)),
|
||||
1, userauth_password, ctx);
|
||||
|
||||
if (rc == 0)
|
||||
lua_pushboolean(L, 1);
|
||||
@@ -672,7 +665,95 @@ static int userauth_password (lua_State *L, int status, lua_KContext ctx) {
|
||||
}
|
||||
|
||||
static int l_userauth_password (lua_State *L) {
|
||||
return userauth_password(L, 0, 0);
|
||||
ssh_userdata *state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
userauth_context *context = &state->userauth;
|
||||
context->username = luaL_checklstring(L, 2, &context->username_len);
|
||||
context->privkey = luaL_checklstring(L, 3, &context->privkey_len);
|
||||
|
||||
return userauth_password(L, 0, (lua_KContext)state);
|
||||
}
|
||||
|
||||
void kbdint_callback(const char *name, int name_len,
|
||||
const char *instruction, int instruction_len,
|
||||
int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
|
||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, void **abstract) {
|
||||
ssh_userdata *state = (ssh_userdata *)*abstract;
|
||||
lua_State *L = state->L;
|
||||
userauth_context *context = &state->userauth;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, context->kbdint_callback_ref);
|
||||
lua_pushlstring(L, context->username, context->username_len);
|
||||
lua_pushlstring(L, name, name_len);
|
||||
lua_pushlstring(L, instruction, instruction_len);
|
||||
lua_createtable(L, num_prompts, 0); // prompts
|
||||
for (int i=0; i < num_prompts; i++) {
|
||||
lua_pushlstring(L, (const char *)prompts[i].text, prompts[i].length);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
int rc = lua_pcall(L, 4, 1, 0);
|
||||
if (rc == LUA_OK && lua_istable(L, -1)) {
|
||||
for (int i=0; i < num_prompts; i++) {
|
||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE *r = &responses[i];
|
||||
r->text = NULL;
|
||||
r->length = 0;
|
||||
lua_geti(L, -1, i+1);
|
||||
size_t len = 0;
|
||||
const char *txt = lua_tolstring(L, -1, &len);
|
||||
if (txt) {
|
||||
/* libssh2 frees this for us */
|
||||
r->text = (char *)malloc(len);
|
||||
if (r->text) {
|
||||
memcpy(r->text, txt, len);
|
||||
r->length = len;
|
||||
}
|
||||
else {
|
||||
r->length = 0;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
// Remove the response table.
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
// else leave the error on the stack.
|
||||
}
|
||||
|
||||
static int keyboard_interactive (lua_State *L, int status, lua_KContext ctx) {
|
||||
ssh_userdata *state = (ssh_userdata *)ctx;
|
||||
userauth_context *context = &state->userauth;
|
||||
int rc;
|
||||
int oldtop = lua_gettop(L);
|
||||
|
||||
DO_OR_YIELD((rc = libssh2_userauth_keyboard_interactive_ex(state->session,
|
||||
context->username, context->username_len, kbdint_callback)),
|
||||
1, keyboard_interactive, ctx);
|
||||
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, context->kbdint_callback_ref);
|
||||
context->kbdint_callback_ref = LUA_NOREF;
|
||||
if (rc == 0) {
|
||||
// Ignore any returned error from the callback,
|
||||
// since auth succeeded anyway
|
||||
lua_settop(L, oldtop);
|
||||
lua_pushboolean(L, 1);
|
||||
}
|
||||
else {
|
||||
int numret = oldtop - lua_gettop(L);
|
||||
lua_pushboolean(L, (rc == 0));
|
||||
// Return any errors, too
|
||||
lua_insert(L, oldtop + 1);
|
||||
return numret + 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_userauth_keyboard_interactive (lua_State *L) {
|
||||
ssh_userdata *state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
|
||||
userauth_context *context = &state->userauth;
|
||||
context->username = luaL_checklstring(L, 2, &context->username_len);
|
||||
luaL_checkany(L, 3);
|
||||
lua_pushvalue(L, 3); // put callback on top of stack
|
||||
context->kbdint_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); // pop callback and get ref
|
||||
return keyboard_interactive(L, 0, (lua_KContext)state);
|
||||
}
|
||||
|
||||
static int session_close (lua_State *L, int status, lua_KContext ctx) {
|
||||
@@ -945,6 +1026,7 @@ static const struct luaL_Reg libssh2[] = {
|
||||
{ "read_publickey", l_read_publickey },
|
||||
{ "publickey_canauth", l_publickey_canauth },
|
||||
{ "userauth_password", l_userauth_password },
|
||||
{ "userauth_keyboard_interactive", l_userauth_keyboard_interactive },
|
||||
{ "session_close", l_session_close },
|
||||
{ "open_channel", l_open_channel},
|
||||
{ "channel_request_pty_ex", l_channel_request_pty_ex},
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
local stdnse = require "stdnse"
|
||||
local table = require "table"
|
||||
local tableaux = require "tableaux"
|
||||
|
||||
local libssh2 = stdnse.silent_require "libssh2"
|
||||
|
||||
@@ -81,7 +82,7 @@ function SSHConnection:run_remote (cmd, no_pty)
|
||||
end
|
||||
|
||||
---
|
||||
-- Attempts to authenticate using provided credentials.
|
||||
-- Attempts to authenticate using password authentication.
|
||||
--
|
||||
-- @param username A username to authenticate as.
|
||||
-- @param password A password to authenticate as.
|
||||
@@ -98,6 +99,66 @@ function SSHConnection:password_auth (username, password)
|
||||
end
|
||||
end
|
||||
|
||||
local function kbd_get_cb(func)
|
||||
return function(username, name, instruction, prompts)
|
||||
local responses = {}
|
||||
for i=1, #prompts do
|
||||
stdnse.debug2("Auth for %s: '%s' '%s' '%s'",
|
||||
username, name, instruction, prompts[i])
|
||||
responses[i] = func(username, name, instruction, prompts[i])
|
||||
stdnse.debug2("Response: %s", responses[i])
|
||||
end
|
||||
return responses
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Attempts to authenticate using keyboard-interactive authentication.
|
||||
--
|
||||
-- @param username A username to authenticate as.
|
||||
-- @param callback A callback function that takes 4 inputs (username, name,
|
||||
-- instruction, prompt) and returns one string response.
|
||||
-- @return true on success or false on failure.
|
||||
function SSHConnection:interactive_auth (username, callback)
|
||||
if not self.session then
|
||||
return false
|
||||
end
|
||||
if libssh2.userauth_keyboard_interactive(self.session, username, kbd_get_cb(callback)) then
|
||||
self.authenticated = true
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Attempts to authenticate using provided credentials.
|
||||
--
|
||||
-- Lists available userauth methods and attempts to authenticate with the given
|
||||
-- credentials. If password authentication is not available, it will use
|
||||
-- keyboard-interactive authentication, responding with <code>password</code>
|
||||
-- to any prompts.
|
||||
--
|
||||
-- @param username A username to authenticate as.
|
||||
-- @param password A password
|
||||
-- @return true on success or false on failure.
|
||||
-- @return the successful auth method, or all methods available. If nil, no methods were found.
|
||||
function SSHConnection:login (username, password)
|
||||
local methods = self:list(username)
|
||||
if not methods then
|
||||
return false
|
||||
end
|
||||
if (tableaux.contains(methods, "password")
|
||||
and self:password_auth(username, password)) then
|
||||
return true, "password"
|
||||
end
|
||||
if (tableaux.contains(methods, "keyboard-interactive") and
|
||||
self:interactive_auth(username, function() return password end)) then
|
||||
return true, "keyboard-interactive"
|
||||
end
|
||||
return false, methods
|
||||
end
|
||||
|
||||
---
|
||||
-- Attempts to authenticate using provided private key file.
|
||||
--
|
||||
|
||||
@@ -38,15 +38,34 @@ function set_timeout(session, timeout)
|
||||
-- @return List of supported authentication methods/
|
||||
function userauth_list(session)
|
||||
|
||||
--- Attempts to authenicate libssh2 session using provided credentials
|
||||
-- @param username Username to authenicate as.
|
||||
-- @param password Password to authenicate with.
|
||||
--- Attempts to authenticate libssh2 session using provided credentials
|
||||
-- @param username Username to authenticate as.
|
||||
-- @param password Password to authenticate with.
|
||||
-- @return true/false, depending on success
|
||||
function userauth_password(session, username, password)
|
||||
|
||||
--- Attempts to authenticate libssh2 session using keyboard-interactive auth
|
||||
-- @param username Username to authenticate as.
|
||||
-- @param callback See <code>userauth_keyboard_interactive_cb</code>
|
||||
-- @return true/false, depending on success
|
||||
-- @return if first return was false, any errors thrown by the callback
|
||||
function userauth_keyboard_interactive(session, username, callback)
|
||||
|
||||
--- Callback function for keyboard interaction.
|
||||
--
|
||||
-- This function is not implemented; it is up to you to write the appropriate
|
||||
-- function. The function must not yield, and so must not use any socket
|
||||
-- functions.
|
||||
-- @param username string Username this authentication is for
|
||||
-- @param name string from the server
|
||||
-- @param instruction string from the server
|
||||
-- @param prompts A table of strings, each of which is a prompt from the server.
|
||||
-- @return a series of response strings, each corresponding to a prompt
|
||||
function userauth_keyboard_interactive_cb(username, name, instruction, prompts)
|
||||
|
||||
--- Attempts to authenticate libssh2 session using provided publickey
|
||||
-- @param session Connected libssh2 session
|
||||
-- @param username Username to authenicate as
|
||||
-- @param username Username to authenticate as
|
||||
-- @param privatekeyfile File containing privatekey
|
||||
-- @param passphrase Passphrase for privatekey
|
||||
-- @param publickeyfile File containing publickey. Not necessary if libssh2 is
|
||||
@@ -62,7 +81,7 @@ function read_publickey(publickeyfile)
|
||||
--- Checks to see if ssh server accepts public key for authentication as given user.
|
||||
-- This doesn't require the private key as it doesn't finish authenticating.
|
||||
-- @param session Connected libssh2 session
|
||||
-- @param username Username to authenicate as
|
||||
-- @param username Username to authenticate as
|
||||
-- @param publickeydata String containing raw publickey blob
|
||||
-- @return true/false, depending on whether user can authenticate with given key
|
||||
function publickey_canauth(session, username, publickeydata)
|
||||
|
||||
Reference in New Issue
Block a user