Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let user handle keyboard-interactive events #192

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions examples/keyboard_interactive_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/python

"""Example script for authentication with password"""

from __future__ import print_function

import argparse
import socket
import os
import pwd
import functools


from ssh2.session import Session


USERNAME = pwd.getpwuid(os.geteuid()).pw_name

parser = argparse.ArgumentParser()

parser.add_argument('password', help="User password")
parser.add_argument('oauth', help="OAUTH key to use for authentication")
parser.add_argument('cmd', help="Command to run")
parser.add_argument('--host', dest='host',
default='localhost',
help='Host to connect to')
parser.add_argument('--port', dest='port', default=22, help="Port to connect on", type=int)
parser.add_argument('-u', dest='user', default=USERNAME, help="User name to authenticate as")


def oauth_handler(name, instruction, prompts, password, oauth):
responses = []

for prompt in prompts:
if "Password:" in prompt:
responses.append(password)
if "One-time password (OATH) for" in prompt:
responses.append(oauth)

return responses

def main():
args = parser.parse_args()

callback = functools.partial(oauth_handler,password=args.password,oauth=args.oauth)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((args.host, args.port))
s = Session()
s.handshake(sock)
s.userauth_keyboardinteractive_callback(args.user, callback)
chan = s.open_session()
chan.execute(args.cmd)
size, data = chan.read()
while size > 0:
print(data)
size, data = chan.read()


if __name__ == "__main__":
main()
52 changes: 36 additions & 16 deletions ssh2/session.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

from cpython cimport PyObject_AsFileDescriptor
from libc.stdlib cimport malloc, free
from libc.stdlib cimport calloc, malloc, free
from libc.string cimport memcpy
from libc.time cimport time_t
from cython.operator cimport dereference as c_dereference

Expand All @@ -26,7 +27,7 @@ from exceptions import SessionHostKeyError, KnownHostError, \
from listener cimport PyListener
from sftp cimport PySFTP
from publickey cimport PyPublicKeySystem
from utils cimport to_bytes, to_str, handle_error_codes
from utils cimport to_bytes, to_str, to_str_len, handle_error_codes
from statinfo cimport StatInfo
from knownhost cimport PyKnownHost
from fileinfo cimport FileInfo
Expand Down Expand Up @@ -79,16 +80,25 @@ cdef void kbd_callback(const char *name, int name_len,
py_sess = (<Session>c_dereference(abstract))
if py_sess._kbd_callback is None:
return
cdef bytes b_password = to_bytes(py_sess._kbd_callback())
cdef size_t _len = len(b_password)
cdef char *_password = b_password
cdef char *_password_copy
if num_prompts == 1:
_password_copy = <char *>malloc(sizeof(char) * _len)
for i in range(_len):
_password_copy[i] = _password[i]
responses[0].text = _password_copy
responses[0].length = _len

cdef list py_prompts = []
for i in range(num_prompts):
prompt_len = prompts[i].length
py_prompts.append(to_str_len(prompts[i].text,prompt_len))

cdef list py_responses = py_sess._kbd_callback(<bytes> name[:name_len], <bytes> instruction[:instruction_len], py_prompts)

cdef bytes response
for i in range(num_prompts):
response = to_bytes(py_responses[i])

_len = len(response)
_buff = <char *>calloc(sizeof(char), _len)
for j in range(_len):
_buff[j] = response[j]

responses[i].text = _buff
responses[i].length = _len


cdef class Session:
Expand Down Expand Up @@ -313,6 +323,19 @@ cdef class Session:
password not None):
"""Perform keyboard-interactive authentication

:param username: User name to authenticate.
:type username: str
:param password: Password
:type password: str
"""
def passwd(*args,password=password):
return [password]
return self.userauth_keyboardinteractive_callback(username, passwd)

def userauth_keyboardinteractive_callback(self, username not None,
callback not None):
"""Perform keyboard-interactive authentication

:param username: User name to authenticate.
:type username: str
:param password: Password
Expand All @@ -322,10 +345,7 @@ cdef class Session:
cdef bytes b_username = to_bytes(username)
cdef const char *_username = b_username

def passwd():
return password

self._kbd_callback = passwd
self._kbd_callback = callback
rc = c_ssh2.libssh2_userauth_keyboard_interactive(
self._session, _username, &kbd_callback)
self._kbd_callback = None
Expand Down