aboutsummaryrefslogtreecommitdiff
path: root/doc/tutorial.dox
blob: 7ab3318bbe257f355dd70a5cb449187e96ff8081 (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
/**
 * @page tutorial The Tutorial
 *
 * @section introduction Introduction
 *
 * Before inserting SSH hooks into your programs, you must know some basics
 * about the SSH protocol, and understand why the SSH library must implement
 * them. A lot of the protocol specifications are hidden by the SSH library API
 * (of course, that's the benefits of using a library) but some still needs an 
 * attention from the programmer.
 *
 * The SSH protocol was designed for some goals which I state here :
 *
 *   - Privacy of data
 *   - Data integrity (alteration of data is always detected)
 *   - Authentication of the server
 *   - Authentication of the client
 *
 * The following document explains how to set up a client-side connection. libssh
 * supports both client and server side of the SSH protocol. If you are going to
 * program a server, I suggest you try first to code some client-side programs
 * in order to understand how libssh works.
 * 
 * The client MUST be sure who's speaking to before entering into any
 * authentication way. That's where the end programmer must ensure the given
 * fingerprints *are* from the legitimate server. A SSH connection must follow
 * the following steps:
 *
 *  - Before connecting the socket, you can set up if you wish one or other
 *  server public key authentication i.e. DSA or RSA. You can choose
 *  cryptographic algorithms you trust and compression algorithms if any. You
 *  must of course set up the hostname
 *
 *  - The connection is made. A secure handshake is made, and resulting from
 *  it, a public key from the server is gained. You MUST verify that the public
 *  key is legitimate, using for instance the MD5 fingerprint of the known hosts
 *  file.
 * 
 *  - The client must authenticate : the classical ways are password and
 *  public keys (from dsa and rsa key-pairs generated by openssh). It is
 *  harmless to authenticate to a fake server with these keys because the
 *  protocol ensures the data you sign can't be used twice. It just avoids
 *  man-in-the-middle attacks. If a SSH-agent is running, it is possible
 *  to use it.
 *
 *   - Now that the user has been authenticated, you must open one or several
 *   channels. channels are different subways for information into a single ssh
 *   connection. Each channel has a standard stream (stdout) and an error stream
 *   (stderr). You can theoretically open an infinity of channel.
 *
 *   - With the channel you opened, you can do several things :
 *     - Open a shell. You may want to request a pseudo virtual terminal before,
 *     - Execute a single command,
 *     - Invoke the sftp subsystem.
 *     - invoke your own subsystem. This is out the scope of this document but it is easy to do. 
 *
 *   - When everything is finished, just close the channels, and then the connection. 
 *
 * At every place, a function which returns an error code (typically SSH_ERROR for int
 * values, NULL for pointers) also sets an error message and an error code.
 *  
 *
 * @section setup Creating the session and setting options
 *
 * The most important object in a SSH connection is the SSH session. In order to allocate a new
 * SSH session, you must use ssh_new(). Don't forget to always verify that the allocation
 * successed.
 * @code
 * #include <libssh/libssh.h> 
 * int main(){
 *   ssh_session my_ssh_session = ssh_new();
 *   if(my_ssh_session == NULL)
 *     exit(-1);
 *   ...
 *   ssh_free(my_ssh_session);
 * @endcode
 * 
 * libssh follows the allocate-it-deallocate-it pattern. Each object that you allocate
 * using xxxxx_new() must be deallocated using xxxxx_free(). In this case, ssh_free() does this.
 * 
 * The ssh_options_set() is a setter for the options of the session. The most important options are the
 * following :
 *   - SSH_OPTIONS_HOST
 *   - SSH_OPTIONS_PORT
 *   - SSH_OPTIONS_USER
 *   - SSH_OPTIONS_LOG_VERBOSITY
 *   - ...
 *
 * The complete list can be found in the documentation of ssh_options_set(). The only mandatory option is
 * SSH_OPTIONS_HOST. If you don't use SSH_OPTIONS_USER, the local username of your account will be used. 
 * Here is a small example of how to use it:
 * @code
 * #include <libssh/libssh.h> 
 * int main(){
 *   int verbosity = SSH_LOG_PROTOCOL;
 *   int port = 22;
 *   ssh_session my_ssh_session = ssh_new();
 *   if(my_ssh_session == NULL)
 *     exit(-1);
 *   ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost");
 *   ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
 *   ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_PORT, &port);
 *   ...
 *   ssh_free(my_ssh_session);
 * @endcode
 * It's no more complicated than that. A few pointers on additionnal functions related to the session 
 * and options setting :
 * @see ssh_new
 * @see ssh_free
 * @see ssh_options_set
 * @see ssh_options_parse_config
 * @see ssh_options_copy
 * @see ssh_options_getopt
 * @section connect Connecting to the server
 *
 * Once all settings have been made, you can connect using ssh_connect(). That function will return 
 * SSH_OK if the connection worked, SSH_ERROR otherwise.
 * You can get the error string using ssh_get_error() in order to show the user what went wrong. Then, use
 * ssh_disconnect() when you want to stop the session. Here's an example:
 *
 * @code
 * #include <libssh/libssh.h>
 * #include <stdio.h> 
 * int main(){
 *   int ret;
 *   ssh_session my_ssh_session = ssh_new();
 *   if(my_ssh_session == NULL)
 *     exit(-1);
 *   ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost");
 *   ret = ssh_connect(my_ssh_session);
 *   if(ret != SSH_OK)
 *     fprintf(stderr, "Error connecting to localhost : %s\n",
 *             ssh_get_error(my_ssh_session));
 *   else
 *     ssh_disconnect(my_ssh_session);
 *   ssh_free(my_ssh_session);
 * @endcode
 * 
 * Once you're connected, the following step is mandatory : you must check that the server
 * you just connected to is known and safe to use (remember, SSH is about security and
 * authentication).
 * 
 * There are two ways of doing this. The first way (recommended) is to use the ssh_is_server_known()
 * function. This function will check into the known host file (~/.ssh/known_hosts on unix)
 * for the server hostname's pattern and check that this host is present or not in the list.
 *
 * The second way is of using ssh_get_pubkey_hash() to get a binary version of the public key hash.
 * You can then use your own database to check if this public key is known and secure. You can also
 * use that function to show the public key hash to the user, in case he knows what the public
 * key hash is (some paranoid people writes their public key hashes on paper before going abroad,
 * just in case ...).
 *
 * Once you concluded that the host is valid and worth being added in the known hosts file, just use
 * ssh_write_knownhost(). The following example is part of the examples suite available in the examples/
 * directory.
 * 
 * @include knownhosts.c
 *
 * @see ssh_connect
 * @see ssh_disconnect
 * @see ssh_get_error
 * @see ssh_get_error_code
 * @see ssh_get_pubkey_hash
 * @see ssh_is_server_known
 * @see ssh_write_knownhost
 *
 * @section auth Authentication
 *
 * The authentication process is the way a service provider can identify a user and verify its 
 * identity. The authorization process is about authorizing the authenticated user the access 
 * to ressources. In SSH, the two concepts are linked. After authentication, the server can
 * allow the user access to several ressources such as port forwarding, shell, sftp subsystem,
 * ...
 * libssh supports several authentication ways:
 *  - "none" method. This method serves to get the available authentications methods, as well as
 *    giving a chance to the server to authenticate the user with just his login. Some very old
 *    hardware are using this feature to fallback the user on a "telnet over SSH" style of login.
 *  - password method. A password is sent to the server, which accepts it or not
 *  - keyboard-interactive method. The server sends several challenges to the user, who must answer 
 *    correctly.
 *    This makes possible the authentication via a codebook for instance (give code at 23:R at page 3).
 *  - public key method. The host knows the public key of the user, and the user must prove he knows the
 *    associated private key. This can be done manually, or delegated by the SSH agent as we'll see
 *    later.
 * 
 * And last but not least, all of this methods can be combined. You can for instance force the user to
 * authenticate with at least two of the authentication methods. In that case, we speak of "Partial 
 * authentication". A partial authentication is a response from authentication functions stating that
 * your credential was accepted, but yet another one is required to get in.
 *
 *
 * @subsection pubkeys Public keys 
 * 
 * The public key authentication is the only method which does not compromise your key if the remote host
 * has been compromised (the server can't do anything more than getting your public key). This is not the 
 * case of a password authentication (the server can get your plaintext password).
 * libssh is fully compatible with the openssh public and private keys. You can either use the automatic
 * public key authentication method provided by libssh, or roll your own using the public key functions.
 * 
 * The process of authenticating by public key to a server is the following :
 * you scan a list of files which contain public keys. each key is sent to the SSH server, until the server 
 * acknowledges a key (a key it knows can be used to authenticate the user). 
 * Then, you retrieve the private key for this key and send a message proving you know that private key. 
 * The function ssh_userauth_autopubkey() does this using the available keys in "~/.ssh/". 
 * The return values are the following:
 *  - SSH_AUTH_ERROR : some serious error happened during authentication
 *  - SSH_AUTH_DENIED : no key matched
 *  - SSH_AUTH_SUCCESS : you are now authenticated
 *  - SSH_AUTH_PARTIAL : some key matched but you still have to give an other mean of authentication 
      (like password). 
 * 
 * The ssh_userauth_autopubkey function also tries to authenticate using the SSH agent, if you have one
 * running, or the "none" method.
 *
 * If you wish to authenticate by public key by your own, follow these steps :
 *   - Retrieve the public key in a ssh_string using publickey_from_file().
 *   - Offer the public key to the SSH server using ssh_userauth_offer_pubkey(). If the return value is
 *     SSH_AUTH_SUCCESS, the SSH server accepts to authenticate using the public key and you can
 *     go to the next step.
 *   - Retrieve the private key, using the privatekey_from_file() function. If a passphrase is needed, either
 *     The passphrase specified as argument or a callback (see callbacks section) will be used.
 *   - Authenticate using ssh_userauth_pubkey() with your public key string and private key.
 *   - Do not forget cleaning up memory using string_free() and privatekey_free(). 
 * 
 * @see ssh_userauth_autopubkey
 * @see ssh_userauth_offer_pubkey
 * @see ssh_userauth_pubkey
 * @see publickey_from_file
 * @see publickey_from_privatekey
 * @see string_free
 * @see privatekey_from_file
 * @see privatekey_free
 * 
 * @subsection password Password
 * 
 * The function ssh_userauth_password() serves the purpose of authenticating using a password. 
 * It will return SSH_AUTH_SUCCESS if the password worked, one of other constants otherwise. It's 
 * your work to ask the password and to deallocate it in a secure manner. If your server complains 
 * that the password is wrong, but you can still authenticate using openssh's client (issuing password), 
 * it's probably because openssh only accept keyboard-interactive. 
 * Switch to keyboard-interactive authentication or try to configure plaintext-passwords into the SSH server.
 * 
 * @see ssh_userauth_password
 * 
 * @subsection keyb-int Keyboard-interactive
 * 
 * The keyboard-interactive method is, as its name tells, interactive. The server will issue one or more
 * challenges, which the used has to answer, until the server takes an authentication decision.
 * ssh_userauth_kbdint() is the the main keyboard-interactive function. 
 * It will return SSH_AUTH_SUCCESS,SSH_AUTH_DENIED, SSH_AUTH_PARTIAL, SSH_AUTH_ERROR, or SSH_AUTH_INFO depending 
 * on the result of the request.
 *
 * The keyboard-interactive authentication method of SSH2 is a feature which permits the server
 * to ask a certain number of questions in an interactive manner to the client, until it decides to 
 * accept or deny the login.
 *
 * To begin, you call this function (just put user and submethods to NULL) and store the answer. If the answer is
 * SSH_AUTH_INFO, it means the server has sent a few questions to ask your user, which you can retrieve with 
 * the following functions : ssh_userauth_kbdint_getnprompts(), ssh_userauth_kbdint_getname(), 
 * ssh_userauth_kbdint_getinstruction() and ssh_userauth_kbdint_getprompt().
 * Set the answer for each question in the challenge using ssh_userauth_kbdint_setanswer(). Then,
 * call back ssh_userauth_kbdint() and start the process again until this functions returns something else
 * than SSH_AUTH_INFO.
 * 
 * Few remarks :
 *  - Even the first call can return SSH_AUTH_DENIED or SSH_AUTH_SUCCESS.
 *  - The server can send an empty question set (this is the default behavior on my system) after you have 
 *    sent the answers to the first questions. you must still parse the answer, it might contain some 
 *    message from the server saying hello or such things. Just call ssh_userauth_kbdint() until needed. 
 *  - The meaning of "name", "prompt", "instruction" may be a little confusing. And explanation is given
 *    in the following documentation.
 * A little note about how to use the informations from keyboard-interactive authentication, coming from
 * The RFC itself (rfc4256) :
 * 
 * @verbatim

  3.3 User Interface Upon receiving a request message, the client SHOULD prompt the user as follows:
  A command line interface (CLI) client SHOULD print the name and instruction (if non-empty), adding newlines.
  Then for each prompt in turn, the client SHOULD display the prompt and read the user input.
  
  A graphical user interface (GUI) client has many choices on how to prompt the user. One possibility is to use 
  the name field (possibly prefixed with the application's name) as the title of a dialog window in which 
  the prompt(s) are presented. In that dialog window, the instruction field would be a text message, and the
  prompts would be labels for text entry fields. All fields SHOULD be presented to the user, for example an 
  implementation SHOULD NOT discard the name field because its windows lack titles; it SHOULD instead find another 
  way to display this information. If prompts are presented in a dialog window, then the client SHOULD NOT
  present each prompt in a separate window.
  
  All clients MUST properly handle an instruction field with embedded newlines. They SHOULD also be able to display
  at least 30 characters for the name and prompts. If the server presents names or prompts longer than 30
  characters, the client MAY truncate these fields to the length it can display. If the client does truncate any
  fields, there MUST be an obvious indication that such truncation has occured.
  
  The instruction field SHOULD NOT be truncated.
  Clients SHOULD use control character filtering as discussed in [SSH-ARCH] to avoid attacks by including terminal
  control characters in the fields to be displayed.
  
  For each prompt, the corresponding echo field indicates whether or not the user input should be echoed as 
  characters are typed. Clients SHOULD correctly echo/mask user input for each prompt independently of other 
  prompts in the request message. If a client does not honor the echo field for whatever reason, then the client 
  MUST err on the side of masking input. A GUI client might like to have a checkbox toggling echo/mask. Clients
  SHOULD NOT add any additional characters to the prompt such as ": " (colon-space); the server is responsible for
  supplying all text to be displayed to the user. Clients MUST also accept empty responses from the user and pass
  them on as empty strings.
 @endverbatim
 * @see ssh_userauth_kbdint()
 * @see ssh_userauth_kbdint_getnprompts
 * @see ssh_userauth_kbdint_getname 
 * @see ssh_userauth_kbdint_getinstruction
 * @see ssh_userauth_kbdint_getprompt
 * @see ssh_userauth_kbdint_setanswer()
 *
 * @subsection none "none" 
 *
 * In fact this mode only serve to get the list of supported authentications, or to be authenticated  
 * **without** any credental. It happens on anonymous services for an example. You can try authentication
 * with this mode by using ssh_userauth_none().
 * It also serves to get the banner message from the server, if any. You should always firstly try this method,
 * at least for getting the banner, then to enter if there is no password at all. You're advised to use
 * ssh_userauth_autopubkey instead.
 * if the account has no password (and the server is configured to let you pass), ssh_userauth_none might 
 * answer SSH_AUTH_SUCCESS.

 * @subsection banner Issue banner
 * ssh_get_banner() retrieves the issue banner that a server may send. That banner may be receive right after a
 * call to ssh_userauth_none().
 * 
 * @subsection auth_example example
 * The following code is an example of authentication using libssh. It's the content of the examples/authentication.c
 * file.
 *
 * @include authentication.c
 */