Skip to content

Commit 84fa52d

Browse files
author
Pan
committed
Implemented public/private key authentication functions.
Added authentication exceptions. Added authentication tests.
1 parent ad8b56e commit 84fa52d

File tree

8 files changed

+116
-64
lines changed

8 files changed

+116
-64
lines changed

ssh/exceptions.pyx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ class AuthenticationError(BaseSSHError):
4343
"""Raised on fatal errors authenticating"""
4444

4545

46+
class AuthenticationPartial(BaseSSHError):
47+
"""Raised on partial authentication"""
48+
49+
4650
class KeyExportError(BaseSSHError):
4751
"""Raised on errors exporting key"""
4852

4953

5054
class KeyImportError(BaseSSHError):
5155
"""Raised on errors importing key"""
5256

53-
57+
5458
class KeyGenerationError(BaseSSHError):
5559
"""Raised on errors generating key"""
56-

ssh/key.pyx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ cdef class SSHKey:
6262
equal = c_ssh.ssh_key_cmp(
6363
self._key, other._key, c_ssh.ssh_keycmp_e.SSH_KEY_CMP_PRIVATE) \
6464
if is_private else \
65-
c_ssh.ssh_key_cmp(
66-
self._key, other._key,
67-
c_ssh.ssh_keycmp_e.SSH_KEY_CMP_PUBLIC)
68-
return bool(equal)
65+
c_ssh.ssh_key_cmp(
66+
self._key, other._key,
67+
c_ssh.ssh_keycmp_e.SSH_KEY_CMP_PUBLIC)
68+
return bool(not equal)
6969

7070
def key_type(self):
7171
cdef c_ssh.ssh_keytypes_e _type
@@ -94,7 +94,7 @@ cdef class SSHKey:
9494
rc = c_ssh.ssh_pki_export_privkey_file(
9595
self._key, c_passphrase, NULL, NULL, c_filepath)
9696
if rc != c_ssh.SSH_OK:
97-
raise KeyExportError(c_ssh.ssh_get_error(self._key))
97+
raise KeyExportError
9898

9999
def export_privkey_to_pubkey(self):
100100
cdef SSHKey pub_key
@@ -103,7 +103,7 @@ cdef class SSHKey:
103103
with nogil:
104104
rc = c_ssh.ssh_pki_export_privkey_to_pubkey(self._key, &_pub_key)
105105
if rc != c_ssh.SSH_OK:
106-
raise KeyExportError(c_ssh.ssh_get_error(self._key))
106+
raise KeyExportError
107107
pub_key = SSHKey.from_ptr(_pub_key)
108108
return pub_key
109109

@@ -116,7 +116,7 @@ cdef class SSHKey:
116116
rc = c_ssh.ssh_pki_export_pubkey_base64(self._key, &_key)
117117
if rc != c_ssh.SSH_OK:
118118
with gil:
119-
raise KeyExportError(c_ssh.ssh_get_error(self._key))
119+
raise KeyExportError
120120
b_key = _key
121121
c_ssh.ssh_string_free_char(_key)
122122
return b_key
@@ -129,7 +129,7 @@ def generate(KeyType key_type, int bits):
129129
with nogil:
130130
rc = c_ssh.ssh_pki_generate(key_type._type, bits, &_key)
131131
if rc != c_ssh.SSH_OK:
132-
raise KeyGenerationError(c_ssh.ssh_get_error(_key))
132+
raise KeyGenerationError
133133
key = SSHKey.from_ptr(_key)
134134
return key
135135

@@ -148,7 +148,7 @@ def import_privkey_base64(bytes b64_key, passphrase=None):
148148
rc = c_ssh.ssh_pki_import_privkey_base64(
149149
c_key, c_passphrase, NULL, NULL, &_key)
150150
if rc != c_ssh.SSH_OK:
151-
raise KeyImportError(c_ssh.ssh_get_error(_key))
151+
raise KeyImportError
152152
key = SSHKey.from_ptr(_key)
153153
return key
154154

@@ -168,7 +168,7 @@ def import_privkey_file(filepath, passphrase=None):
168168
rc = c_ssh.ssh_pki_import_privkey_file(
169169
c_filepath, c_passphrase, NULL, NULL, &_key)
170170
if rc != c_ssh.SSH_OK:
171-
raise KeyExportError(c_ssh.ssh_get_error(_key))
171+
raise KeyImportError
172172
key = SSHKey.from_ptr(_key)
173173
return key
174174

@@ -182,7 +182,7 @@ def import_pubkey_base64(bytes b64_key, KeyType key_type):
182182
rc = c_ssh.ssh_pki_import_pubkey_base64(
183183
c_key, key_type._type, &_key)
184184
if rc != c_ssh.SSH_OK:
185-
raise KeyImportError(c_ssh.ssh_get_error(_key))
185+
raise KeyImportError
186186
key = SSHKey.from_ptr(_key)
187187
return key
188188

@@ -197,6 +197,6 @@ def import_pubkey_file(filepath):
197197
rc = c_ssh.ssh_pki_import_pubkey_file(
198198
c_filepath, &_key)
199199
if rc != c_ssh.SSH_OK:
200-
raise KeyExportError(c_ssh.ssh_get_error(_key))
200+
raise KeyImportError
201201
key = SSHKey.from_ptr(_key)
202202
return key

ssh/options.pyx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,33 @@ TIMEOUT_USEC = Option.from_option(ssh_options_e.SSH_OPTIONS_TIMEOUT_USEC)
5252
SSH1 = Option.from_option(ssh_options_e.SSH_OPTIONS_SSH1)
5353
SSH2 = Option.from_option(ssh_options_e.SSH_OPTIONS_SSH2)
5454
LOG_VERBOSITY = Option.from_option(ssh_options_e.SSH_OPTIONS_LOG_VERBOSITY)
55-
LOG_VERBOSITY_STR = Option.from_option(ssh_options_e.SSH_OPTIONS_LOG_VERBOSITY_STR)
55+
LOG_VERBOSITY_STR = Option.from_option(
56+
ssh_options_e.SSH_OPTIONS_LOG_VERBOSITY_STR)
5657
CIPHERS_C_S = Option.from_option(ssh_options_e.SSH_OPTIONS_CIPHERS_C_S)
5758
CIPHERS_S_C = Option.from_option(ssh_options_e.SSH_OPTIONS_CIPHERS_S_C)
5859
COMPRESSION_C_S = Option.from_option(ssh_options_e.SSH_OPTIONS_COMPRESSION_C_S)
5960
COMPRESSION_S_C = Option.from_option(ssh_options_e.SSH_OPTIONS_COMPRESSION_S_C)
6061
PROXYCOMMAND = Option.from_option(ssh_options_e.SSH_OPTIONS_PROXYCOMMAND)
6162
BINDADDR = Option.from_option(ssh_options_e.SSH_OPTIONS_BINDADDR)
62-
STRICTHOSTKEYCHECK = Option.from_option(ssh_options_e.SSH_OPTIONS_STRICTHOSTKEYCHECK)
63+
STRICTHOSTKEYCHECK = Option.from_option(
64+
ssh_options_e.SSH_OPTIONS_STRICTHOSTKEYCHECK)
6365
COMPRESSION = Option.from_option(ssh_options_e.SSH_OPTIONS_COMPRESSION)
64-
COMPRESSION_LEVEL = Option.from_option(ssh_options_e.SSH_OPTIONS_COMPRESSION_LEVEL)
66+
COMPRESSION_LEVEL = Option.from_option(
67+
ssh_options_e.SSH_OPTIONS_COMPRESSION_LEVEL)
6568
KEY_EXCHANGE = Option.from_option(ssh_options_e.SSH_OPTIONS_KEY_EXCHANGE)
6669
HOSTKEYS = Option.from_option(ssh_options_e.SSH_OPTIONS_HOSTKEYS)
67-
GSSAPI_SERVER_IDENTITY = Option.from_option(ssh_options_e.SSH_OPTIONS_GSSAPI_SERVER_IDENTITY)
68-
GSSAPI_CLIENT_IDENTITY = Option.from_option(ssh_options_e.SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY)
69-
GSSAPI_DELEGATE_CREDENTIALS = Option.from_option(ssh_options_e.SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS)
70+
GSSAPI_SERVER_IDENTITY = Option.from_option(
71+
ssh_options_e.SSH_OPTIONS_GSSAPI_SERVER_IDENTITY)
72+
GSSAPI_CLIENT_IDENTITY = Option.from_option(
73+
ssh_options_e.SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY)
74+
GSSAPI_DELEGATE_CREDENTIALS = Option.from_option(
75+
ssh_options_e.SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS)
7076
HMAC_C_S = Option.from_option(ssh_options_e.SSH_OPTIONS_HMAC_C_S)
7177
HMAC_S_C = Option.from_option(ssh_options_e.SSH_OPTIONS_HMAC_S_C)
7278
PASSWORD_AUTH = Option.from_option(ssh_options_e.SSH_OPTIONS_PASSWORD_AUTH)
7379
PUBKEY_AUTH = Option.from_option(ssh_options_e.SSH_OPTIONS_PUBKEY_AUTH)
7480
KBDINT_AUTH = Option.from_option(ssh_options_e.SSH_OPTIONS_KBDINT_AUTH)
7581
GSSAPI_AUTH = Option.from_option(ssh_options_e.SSH_OPTIONS_GSSAPI_AUTH)
76-
GLOBAL_KNOWNHOSTS = Option.from_option(ssh_options_e.SSH_OPTIONS_GLOBAL_KNOWNHOSTS)
82+
GLOBAL_KNOWNHOSTS = Option.from_option(
83+
ssh_options_e.SSH_OPTIONS_GLOBAL_KNOWNHOSTS)
7784
NODELAY = Option.from_option(ssh_options_e.SSH_OPTIONS_NODELAY)

ssh/session.pyx

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ from cpython cimport PyObject_AsFileDescriptor
1818
from libc.stdlib cimport malloc, free
1919
from libc.string cimport const_char
2020

21-
from utils cimport to_bytes, to_str, handle_error_codes
21+
from utils cimport to_bytes, to_str, handle_ssh_error_codes, \
22+
handle_auth_error_codes
2223
from options cimport Option
24+
from key cimport SSHKey
2325

2426
from exceptions import OptionError
2527

@@ -43,7 +45,7 @@ cdef class Session:
4345
cdef int rc
4446
with nogil:
4547
rc = c_ssh.ssh_blocking_flush(self._session, timeout)
46-
return handle_error_codes(rc, self._session)
48+
return handle_ssh_error_codes(rc, self._session)
4749

4850
def new_channel(self):
4951
cdef c_ssh.ssh_channel _channel
@@ -54,7 +56,7 @@ cdef class Session:
5456
cdef int rc
5557
with nogil:
5658
rc = c_ssh.ssh_connect(self._session)
57-
return handle_error_codes(rc, self._session)
59+
return handle_ssh_error_codes(rc, self._session)
5860

5961
def disconnect(self):
6062
with nogil:
@@ -78,7 +80,7 @@ cdef class Session:
7880
with nogil:
7981
rc = c_ssh.ssh_channel_cancel_forward(
8082
self._session, c_address, port)
81-
return handle_error_codes(rc, self._session)
83+
return handle_ssh_error_codes(rc, self._session)
8284

8385
def listen_forward(self, address, int port, int bound_port):
8486
cdef bytes b_address = to_bytes(address)
@@ -87,7 +89,7 @@ cdef class Session:
8789
with nogil:
8890
rc = c_ssh.ssh_channel_listen_forward(
8991
self._session, c_address, port, &bound_port)
90-
return handle_error_codes(rc, self._session)
92+
return handle_ssh_error_codes(rc, self._session)
9193

9294
def get_disconnect_message(self):
9395
cdef const char *message
@@ -160,7 +162,7 @@ cdef class Session:
160162
cdef int rc
161163
with nogil:
162164
rc = c_ssh.ssh_options_copy(self._session, &destination._session)
163-
return handle_error_codes(rc, self._session)
165+
return handle_ssh_error_codes(rc, self._session)
164166

165167
def options_getopt(self):
166168
raise NotImplementedError
@@ -171,14 +173,14 @@ cdef class Session:
171173
cdef int rc
172174
with nogil:
173175
rc = c_ssh.ssh_options_parse_config(self._session, c_filepath)
174-
return handle_error_codes(rc, self._session)
176+
return handle_ssh_error_codes(rc, self._session)
175177

176178
def options_set_port(self, int port):
177179
cdef int rc
178180
with nogil:
179181
rc = c_ssh.ssh_options_set(
180182
self._session, c_ssh.ssh_options_e.SSH_OPTIONS_PORT, &port)
181-
return handle_error_codes(rc, self._session)
183+
return handle_ssh_error_codes(rc, self._session)
182184

183185
def options_set(self, Option option, value):
184186
"""Set an option for session.
@@ -193,7 +195,7 @@ cdef class Session:
193195
c_value = b_value
194196
with nogil:
195197
rc = c_ssh.ssh_options_set(self._session, option._option, c_value)
196-
return handle_error_codes(rc, self._session)
198+
return handle_ssh_error_codes(rc, self._session)
197199

198200
def options_get(self, Option option):
199201
cdef char *_value
@@ -214,22 +216,22 @@ cdef class Session:
214216
cdef int rc
215217
with nogil:
216218
rc = c_ssh.ssh_options_get_port(self._session, &port_target)
217-
return handle_error_codes(rc, self._session)
219+
return handle_ssh_error_codes(rc, self._session)
218220

219221
def send_ignore(self, bytes data):
220222
cdef char *c_data = data
221223
cdef int rc
222224
with nogil:
223225
rc = c_ssh.ssh_send_ignore(self._session, c_data)
224-
return handle_error_codes(rc, self._session)
226+
return handle_ssh_error_codes(rc, self._session)
225227

226228
def send_debug(self, bytes message, int always_display):
227229
cdef char *c_message = message
228230
cdef int rc
229231
with nogil:
230232
rc = c_ssh.ssh_send_debug(
231233
self._session, c_message, always_display)
232-
return handle_error_codes(rc, self._session)
234+
return handle_ssh_error_codes(rc, self._session)
233235

234236
def gssapi_set_creds(self, creds):
235237
raise NotImplementedError
@@ -239,7 +241,7 @@ cdef class Session:
239241
cdef char *c_service = service
240242
with nogil:
241243
rc = c_ssh.ssh_service_request(self._session, c_service)
242-
return handle_error_codes(rc, self._session)
244+
return handle_ssh_error_codes(rc, self._session)
243245

244246
def set_agent_channel(self, channel):
245247
raise NotImplementedError
@@ -248,7 +250,7 @@ cdef class Session:
248250
cdef int rc
249251
with nogil:
250252
rc = c_ssh.ssh_set_agent_socket(self._session, fd)
251-
return handle_error_codes(rc, self._session)
253+
return handle_ssh_error_codes(rc, self._session)
252254

253255
def set_blocking(self, int blocking):
254256
with nogil:
@@ -278,44 +280,44 @@ cdef class Session:
278280
cdef int rc
279281
with nogil:
280282
rc = c_ssh.ssh_userauth_none(self._session, NULL)
281-
return handle_error_codes(rc, self._session)
283+
return handle_ssh_error_codes(rc, self._session)
282284

283285
def userauth_list(self):
284286
cdef int rc
285287
with nogil:
286288
rc = c_ssh.ssh_userauth_list(self._session, NULL)
287-
return handle_error_codes(rc, self._session)
289+
return handle_ssh_error_codes(rc, self._session)
288290

289-
def userauth_try_publickey(self, username, pubkey):
290-
cdef bytes b_username = to_bytes(username)
291-
cdef char *c_username = b_username
291+
def userauth_try_publickey(self, SSHKey pubkey):
292292
cdef int rc
293-
raise NotImplementedError
293+
with nogil:
294+
rc = c_ssh.ssh_userauth_try_publickey(
295+
self._session, NULL, pubkey._key)
296+
return handle_auth_error_codes(rc, self._session)
294297

295-
def userauth_publickey(self, username, privkey):
296-
cdef bytes b_username = to_bytes(username)
297-
cdef char *c_username = b_username
298+
def userauth_publickey(self, SSHKey privkey):
298299
cdef int rc
299-
raise NotImplementedError
300+
with nogil:
301+
rc = c_ssh.ssh_userauth_publickey(
302+
self._session, NULL, privkey._key)
303+
return handle_auth_error_codes(rc, self._session)
300304

301305
def userauth_agent(self, username):
302306
cdef bytes b_username = to_bytes(username)
303307
cdef char *c_username = b_username
304308
cdef int rc
305309
with nogil:
306310
rc = c_ssh.ssh_userauth_agent(self._session, c_username)
307-
return handle_error_codes(rc, self._session)
311+
return handle_ssh_error_codes(rc, self._session)
308312

309-
def userauth_publickey_auto(self, username, passphrase):
310-
cdef bytes b_username = to_bytes(username)
313+
def userauth_publickey_auto(self, passphrase):
311314
cdef bytes b_passphrase = to_bytes(passphrase)
312-
cdef char *c_username = b_username
313315
cdef char *c_passphrase = b_passphrase
314316
cdef int rc
315317
with nogil:
316318
rc = c_ssh.ssh_userauth_publickey_auto(
317-
self._session, c_username, c_passphrase)
318-
return handle_error_codes(rc, self._session)
319+
self._session, NULL, c_passphrase)
320+
return handle_ssh_error_codes(rc, self._session)
319321

320322
def userauth_password(self, username, password):
321323
cdef bytes b_username = to_bytes(username)
@@ -326,7 +328,7 @@ cdef class Session:
326328
with nogil:
327329
rc = c_ssh.ssh_userauth_password(
328330
self._session, c_username, c_password)
329-
return handle_error_codes(rc, self._session)
331+
return handle_ssh_error_codes(rc, self._session)
330332

331333
def userauth_kbdint(self, username, submethods):
332334
cdef bytes b_username = to_bytes(username)
@@ -337,13 +339,14 @@ cdef class Session:
337339
with nogil:
338340
rc = c_ssh.ssh_userauth_kbdint(
339341
self._session, c_username, c_submethods)
340-
return handle_error_codes(rc, self._session)
342+
return handle_ssh_error_codes(rc, self._session)
341343

342344
def userauth_kbdint_getinstruction(self):
343345
cdef bytes b_instruction
344346
cdef const_char *_instruction
345347
with nogil:
346-
_instruction = c_ssh.ssh_userauth_kbdint_getinstruction(self._session)
348+
_instruction = c_ssh.ssh_userauth_kbdint_getinstruction(
349+
self._session)
347350
b_instruction = to_str(<char *>_instruction)
348351
return b_instruction
349352

@@ -392,19 +395,19 @@ cdef class Session:
392395
with nogil:
393396
rc = c_ssh.ssh_userauth_kbdint_setanswer(
394397
self._session, i, <const_char *>(c_answer))
395-
return handle_error_codes(rc, self._session)
398+
return handle_ssh_error_codes(rc, self._session)
396399

397400
def userauth_gssapi(self):
398401
cdef int rc
399402
with nogil:
400403
rc = c_ssh.ssh_userauth_gssapi(self._session)
401-
return handle_error_codes(rc, self._session)
404+
return handle_ssh_error_codes(rc, self._session)
402405

403406
def write_knownhost(self):
404407
cdef int rc
405408
with nogil:
406409
rc = c_ssh.ssh_write_knownhost(self._session)
407-
return handle_error_codes(rc, self._session)
410+
return handle_ssh_error_codes(rc, self._session)
408411

409412
def dump_knownhost(self):
410413
cdef const_char *_known_host

ssh/utils.pxd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@
1717
cdef bytes to_bytes(_str)
1818
cdef object to_str(char *c_str)
1919
cdef object to_str_len(char *c_str, int length)
20-
cdef int handle_error_codes(int errcode, void *caller) except -1
20+
cdef int handle_ssh_error_codes(int errcode, void *caller) except -1
21+
cdef int handle_auth_error_codes(int errcode, void *caller) except -1

0 commit comments

Comments
 (0)