aboutsummaryrefslogtreecommitdiff
path: root/doc/libssh-0.2-api-1.txt
blob: 22d08e58b6b82e2e8d4707676a11485675b193db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
                    The new libssh 0.2 API
                    ----------------------

Version 1

A. Introduction
---------------

With the time from the first release of libssh, I have received lots of
comments about the current API. Myself, I found it quite limiting when doing
my first libssh-server drafts. Thus, I am moving to a stronger API.
This API must still be simple. I am not introducing complex changes. An API
well designed must hide the implementation details. Implementation can change
easily within bugfixes - but API cannot change each release.

To the people already using libssh 0.11 : sorry. Once I have the complete API
redesigned, I will write a migration paper. It won't be too hard normally.

Here are the things that were lacking in the previous API and *must* change:

* A non-blocking mode connection type
* Functions to relegate File descriptor listening to Calling functions and to
  the programmer. (I'll explain later).
* Along with that, good buffering system (well, it's not an API but).
* Leave the "functions returns a pointer when it works and NULL when it does
  not work". It gives serious problems to implement bindings (A C++
  constructor should not fail and should not depend on a network thing
* Make the Session structure an abstract structure that can work with both
  client and *servers*. That mean we should have a Server object which listen
  to clients on a bound port, does the different handshakes and return a
  session.
  Since C is not per se an Object language, I won't use inheritance between
  objects.
* This same server thing must provide the reverse capabilities than the
  client. That is, accept the handshake, in a nonblocking way. Accept channel
  requests, or send them to the controller program.
* Support for program forking : Imagine you have a Ssh server object. You
  accept a connection and receive a session, then you receive a channel. You
  may want to keep the good old days fork() tricks. Libssh will give a way to
  destroy handlers from sessions which belong to an other process without
  disturbing the session.
* So often I received the comment back saying that it was not clear why a
  session or a channel was terminated. This is over.
* And of course I received lot of mails about the fact I'm doing namespace
  polution. this will be resolved this time.
So, please read this draft not as a formal documentation but like a roadmap of
things that each kind of object must do.

B. Description of objects and functions

Initialization and finalization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Initialization is for now called automatically, so you don't have to take care
of that.
As for finalization, we need to finalize the underlying cryptographic library
(either OpenSSL or libgcrypt). Be sure that you call ssh_finalize when this
library won't be used anymore, even by other libraries (i.e. if you use libssh
and another library that uses OpenSSL, call ssh_finalize when any function of
both these libraries won't be called).
If you trust your operating system to clean up the mess after a process
terminates, you can skip this call.

Options structure
~~~~~~~~~~~~~~~~~

struct ssh_options *ssh_options_new()

ssh_options_getopt(options, *argc, argv)

ssh_options_copy(options)

char ** ssh_options_get_supported_algos(options,type)
 returns a list of the algos supported by libssh, type being one of
 SSH_HOSTKEYS, SSH_KEX, SSH_CRYPT, SSH_MAC, SSH_COMP, SSH_LANG

ssh_options_set_wanted_algos(options,type, char *list)
list being comma-separated list of algos, and type being the upper constants
but with _C_S or _S_V added to them.

ssh_options_set_port(options, port)

ssh_options_set_host(options, host)

ssh_options_set_fd(options, fd)

ssh_options_set_bind(options, bindaddr, port)
this options sets the address to bind for a client *or* a server. a port of
zero means whatever port is free (what most clients want).

ssh_options_set_username(options, username)

ssh_options_set_connect_timeout(options, seconds, usec)

ssh_options_set_ssh_dir(options, dir)
ssh_options_set_known_hosts_file(options, file)
ssh_options_set_identity(options, file)

ssh_options_set_banner(options, banner)
ssh_options_allow_ssh1(options, bool allow)
ssh_options_allow_ssh2(options, bool allow)

options_set_status_callback has moved into ssh_* functions.

ssh_session Structure
~~~~~~~~~~~~~~~~~~~~~

This session structure represents a ssh socket to a server *or* a client.

ssh_session *ssh_new()

ssh_set_options(ssh_session,ssh_options)

ssh_connect(session);
 it will return some status describing at which point of the connection it is,
 or an error code. If the connection method is non-blocking, the function
 will be called more than once, though the return value SSH_AGAIN.

ssh_set_blocking(session, bool blocking)
 set blocking mode or non blocking mode.

ssh_get_fd(session)
 get the currently used connection file descriptor or equivalent (windows)

ssh_set_fd_toread(session)
ssh_set_fd_towrite(session)
ssh_set_fd_except(session)
 Serve to notify the library that data is actualy available to be read on the
 file descriptor socket. why ? because on most platforms select can't be done
 twice on the same socket when the first reported data to read or to write
 
ssh_get_status(session)
 Returns the current status bitmask : connection Open or closed, data
 pending to read or not (even if connection closed), connection closed on
 error or on an exit message

ssh_get_disconnect_message(session)
 Returns the connection disconnect error/exit message

ssh_get_pubkey_hash(session, hash)
 get the public key hash from the server.

ssh_is_server_known(session)
ssh_write_knownhost(session)
 these 2 functions will be kept

ssh_disconnect(session)
 standard disconnect

ssh_disconnect_error(session,error code, message)
 disconnect with a message

ssh_set_username(session)
 set the user name to log in
 
ssh_userauth_* functions will be kept as they are now, excepted the fact that
the username field will disapear.
the public key mechanism may get some more functions, like retrieving a public
key from a private key and authenticating without a public key.

ssh_get_issue_banner(session)
 get the issue banner from the server, that is the welcome message.

ssh_silent_free(session)
 This function silently free all data structures used by the session and
 closes the socket. It may be used for instance when the process forked and
 doesn't want to keep track of this session. This is obviously not possible to
 do with separate channels.

The channel_struct structure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The channels will change a bit. the constructor thing will change, and the way
to multiplex different connections will change too. channel functions will be
prefixed with "ssh_"

struct channel_struct *ssh_channel_new()

ssh_channel_open_session(channel)
 will return if the channel allocation failed or not.

ssh_channel_open_forward(channel, ...) won't change. it will report an error if
the channel allocation failed.

ssh_channel_send_eof(channel)
 send EOF
ssh_channel_close(channel)
 closes a channel but doesn't destroy it. you may read unread data still in
 the buffer. Once you closed the buffer, the other party can't send you data,
 while it could still do it if you only sent an EOF.
ssh_channel_is_closed(channel)
 returns true if the channel was closed at one of both sides. a closed chan
 may still have data to read, if you closed yourself the connection. otherwise
 (you didn't close it) the closed notification only comes when you read the
 last buffer byte, or when trying to write into the channel (the SIGPIPE-like
 behaviour).

ssh_channel_is_eof(channel)
 reports if the other side has sent an EOF. This functions returns FALSE if
 there is still data to read. A closed channel is always EOF.
ssh_channel_free(channel)
 completely free the channel. closes it before if it was not done.

ssh_channel_request_env(channel, name, value)
 set an environment variable.

ssh_channel_request_pty(channel)
ssh_channel_request_pty_size()
ssh_channel_change_pty_size()
ssh_channel_request_shell()
ssh_channel_request_exec()
ssh_channel_request_subsystem()
These functions won't change.

int ssh_channel_write(channel,data, len,stderr)
 Depending on the blocking/non blocking mode of the channel, the behaviour may
 change.
 stderr is the extended buffer. It's generaly only a server->client stream.

ssh_channel_set_blocking(bool blocking)

int ssh_channel_read(channel, buffer, maxlen, is_stderr)
 the behaviour will be this one:
  -if the chan is in non blocking mode, it will poll what's available to read
  and return this. otherwise (nothing to read) it will return 0.
  -if the chan is blocking, it will block until at least one byte is
  available.
ssh_channel_nonblocking disapears for the later reason.

int channel_poll(channel, is_stderr)
 polls the network and reports the number of bytes ready to be read in the
 chan.

ssh_session ssh_channel_get_session(channel)
 returns the session pointer associated to the channel, for simplicity
 reasons.

int ssh_channel_select(CHANNELS *readchans, CHANNELS *writechans, CHANNELS
              *exceptchans, struct timeval *timeout)
 This function won't work the same way ssh_select did.
 I removed the custom file descriptor thing for 2 reasons:
  1- it's not windows compliant. D'ouh !
  2- most programmers won't want to depend on libssh for socket multiplexing.
  that's why i let the programmer poll the fds himself and then use
  ssh_set_fd_toread, towrite or except. Then, he may use ssh_channel_select
  with a NULL timeout to poll which channels have something to read, write or
  error report.
 Here is how it's going to work. The coder sets 3 different arrays with the
 channels he wants to select(), the last entry being a NULL pointer. The
 function will first poll them and return the chans that must be
 read/write/excepted. If nothing has this state, the function will select()
 using the timeout.
 The function will return 0 if everything is ok, SSH_TIMEOUT or SSH_EINTR if
 the select was interrupted by a signal. It is dangerous to execute any
 channel-related functions into signal handlers. they should set a flag that
 you read into your loop. this "trap" (SSH_EINTR) will permit you to catch
 them faster and make your program responsive and look fast.
 the function will return -1 if a serious problem happens.


Error handling
~~~~~~~~~~~~~~

when an error happens, the programmer can get the error code and description
with ssh_get_error(session). the creation of a failess constructor for
ssh_session was needed for this reason.

ssh_get_error_code(session) will return an error code into this subset:
 SSH_NO_ERROR : no error :)
 SSH_REQUEST_DENIED : you request for a functionality or a service that is not
 allowed. The session can continue.
 SSH_FATAL : Unrecoverable error. The session can't continue and you should
 disconnect the session. It includes the connection being cut without a
 disconnect() message.
 If a disconnect() message or the channel was closed, a read on such a channel
 won't produce an error. otherwise it will return -1 with a SSH_FATAL error
 code.

Server socket binding
~~~~~~~~~~~~~~~~~~~~~

It is not possible to bind a socket for ssh with a SSH_SESSION type, because a
single bound port may lead to multiple ssh connections. That's why the
SSH_BIND structure must be created. It uses options from the SSH_OPTIONS
structure.

SSH_BIND *ssh_bind_new()
creates a structure
ssh_bind_set_options(bind, options)
set the option structure
int ssh_bind_listen(bind)
 bind and listen to the port. This call is not blocking. if some error
 happens, it returns -1 and the error code can be found with perror().

ssh_bind_set_blocking(bind, bool blocking)
 should ssh_bind_accept() block or not.

int ssh_bind_get_fd(bind)
 return the bound file descriptor, that is the listener socket. you may put it
 into a select() in your code to detect a connection attempt.

ssh_bind_set_fd_toaccept(bind)
 say that the listener socket has a connection to accept (to avoid
 ssh_bind_accept() to do a select on it).

SSH_SESSION *ssh_bind_accept(bind)
 return a server handle to a ssh session. if the mode is blocking, the
 function will always return a pointer to a session. if the mode is not
 blocking, the function can return NULL if there is no connection to accept.

This SSH_SESSION handle must then pass through the functions explained above.


*server functions *

int ssh_accept(session)
 when a new connection is accepted, the handshake must be done. this function
 will do the banner handshake and the key exchange.
 it will return SSH_AGAIN if the session mode is non blocking, and the
 function must be called again until an error occurs or the kex is done.

Here, I had a few choises about *how* to implement the message parsing as a
server. There are multiple ways to do it, one being callbacks and one being
"Message" reading, parsing and then choice going to the user to use it and
answer. I've choosen the latter because i believe it's the stronger method.
A ssh server can receive 30 different kind of messages having to be dealt by
the high level routines, like channel request_shell or authentication. Having
a callback for all of them would produce a huge kludge of callbacks, with
no relations on when there were called etc.
A message based parsing allows the user to filtrate the messages he's
interested into and to use a default answer for the others. Then, the callback
thing is still possible to handle through a simple message code/callback
function array.

I did not define yet what it would look like, but i'm sure there will be a
SSH_MESSAGE (they won't have a 1/1 correspondance with ssh packets) which will
be read through
SSH_MESSAGE *ssh_server_read_message(session).
with all of the non-blocking stuff in head like returning NULL if the message
is not full.
Then, the message can be parsed, ie
int ssh_message_get_code(message)
which will return SSH_MESSAGE_AUTH
then
int ssh_message_get_subcode(message)
which then will returh SSH_MESSAGE_AUTH_PASSWORD or _NONE or _PUBKEY etc.

Then, once the message was parsed, the message will have to be answered, ie
with the generic functions like
ssh_message_accept(message) which says 'Ok your request is accepted' or
ssh_message_deny(message) which says 'Your request is refused'.

There would be specific message answer functions for some kind of messages
like the authentication one. you may want to reply that the authentication is
Partial rather than denied, and that you still accept some kind of auths, like
ssh_message_auth_reply(message,SSH_AUTH_PARTIAL,SSH_AUTH_PASSWORD |
SSH_AUTH_PUBKEY | SSH_AUTH_KEYBINT);

I won't let the user have to deal with the channels himself. When a channel is
going to be created by the remote size, a message will come asking to open a
channel. the programmer can either deny or accept, in which case a CHANNEL
object will be created and returned to the programmer. then, all standard
channel functions will run.

C. Change log of this document

3. Add paragraph about initalization and finalization.

2. ssh_options_set_username finaly is kept into the options, because it can be
set by ssh_options_getopt()

1. first release

D. End notes

I think libssh must have a very simple to use, powerful and exhaustive API. It
must have no design flaw either.
While I got some good experience at the SSH protocol, I've never writen
more-than-100 lines programs than use libssh and I don't really know the
problems of the library. I'd like people who don't understand some detail into
the API I describe here, who have comments or opinions about it to write me
the soonest possible to limit the damages if I made something the completely
wrong way.
Thanks for your patience.