Server authentication

Once the SSH session is established, we can get the server's public key using the ssh_get_server_publickey() function. The following code illustrates this function call:

/*ssh_auth.c excerpt*/

ssh_key key;
if (ssh_get_server_publickey(ssh, &key) != SSH_OK) {
fprintf(stderr, "ssh_get_server_publickey() failed.\n%s\n",
ssh_get_error(ssh));
return -1;
}

It is often useful to obtain and display a hash of the server's SSH public key. Users can look at hashes and compare these to known keys. The libssh library provides the ssh_get_publickey_hash() function for this purpose.

The following code prints out an SHA1 hash of the public key obtained earlier:

/*ssh_auth.c excerpt*/

unsigned char *hash;
size_t hash_len;
if (ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_SHA1,
&hash, &hash_len) != SSH_OK) {
fprintf(stderr, "ssh_get_publickey_hash() failed.\n%s\n",
ssh_get_error(ssh));
return -1;
}

printf("Host public key hash:\n");
ssh_print_hash(SSH_PUBLICKEY_HASH_SHA1, hash, hash_len);

libssh prints SHA1 hashes using Base64. It also prepends the hash type first. For example, the preceding code might print the following:

Host public key hash:
SHA1:E348CMNeCGGec/bQqEX7aocDTfI

When you've finished with the public key and hash, free their resources with the following code:

/*ssh_auth.c excerpt*/

ssh_clean_pubkey_hash(&hash);
ssh_key_free(key);

libssh provides the ssh_session_is_known_server() function to determine whether a server's public key is known. The following code shows an example of using this code:

/*ssh_auth.c excerpt*/

enum ssh_known_hosts_e known = ssh_session_is_known_server(ssh);
switch (known) {
case SSH_KNOWN_HOSTS_OK: printf("Host Known.\n"); break;

case SSH_KNOWN_HOSTS_CHANGED: printf("Host Changed.\n"); break;
case SSH_KNOWN_HOSTS_OTHER: printf("Host Other.\n"); break;
case SSH_KNOWN_HOSTS_UNKNOWN: printf("Host Unknown.\n"); break;
case SSH_KNOWN_HOSTS_NOT_FOUND: printf("No host file.\n"); break;

case SSH_KNOWN_HOSTS_ERROR:
printf("Host error. %s\n", ssh_get_error(ssh)); return 1;

default: printf("Error. Known: %d\n", known); return 1;
}

If the server's public key is known (previously trusted), then ssh_session_is_known_server() returns SSH_KNOWN_HOSTS_OK. Otherwise, ssh_session_is_known_server() can return other values with various meanings.

SSH_KNOWN_HOSTS_UNKNOWN indicates that the server is unknown. In this case, the user should verify the server's hash.

SSH_KNOWN_HOSTS_NOT_FOUND means that libssh didn't find a hosts file, and one is created automatically. This should generally be treated in the same way as SSH_KNOWN_HOSTS_UNKNOWN.

SSH_KNOWN_HOSTS_CHANGED indicates that the server is returning a different key than was previously known, while SSH_KNOWN_HOSTS_OTHER indicates that the server is returning a different type of key than was previously used. Either of these could indicate a potential attack! In a real-world application, you should be more explicit about notifying the user of these risks.

If the user has verified that a host is to be trusted, use ssh_session_update_known_hosts() to allow libssh to save the servers public key hash. This allows ssh_session_is_known_server() to return SSH_KNOWN_HOSTS_OK for the next connection.

The following code illustrates prompting the user to trust a connection and using ssh_session_update_known_hosts():

/*ssh_auth.c excerpt*/

if (known == SSH_KNOWN_HOSTS_CHANGED ||
known == SSH_KNOWN_HOSTS_OTHER ||
known == SSH_KNOWN_HOSTS_UNKNOWN ||
known == SSH_KNOWN_HOSTS_NOT_FOUND) {
printf("Do you want to accept and remember this host? Y/N\n");
char answer[10];
fgets(answer, sizeof(answer), stdin);
if (answer[0] != 'Y' && answer[0] != 'y') {
return 0;
}

ssh_session_update_known_hosts(ssh);
}

Please see ssh_auth.c in this chapter's code repository for a working example. Consult the libssh documentation for more information.

After the client has authenticated the server, the server needs to authenticate the client.