diff --git a/TODO b/TODO index 560011e..379cb0a 100644 --- a/TODO +++ b/TODO @@ -49,3 +49,5 @@ At next SONAME bump * change 'int' to 'libssh2_socket_t' in the public API for sockets. * Use 'size_t' for string lengths in all functions. + +* Add a comment field to struct libssh2_knownhost. diff --git a/include/libssh2.h b/include/libssh2.h index 000118a..34a8a92 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -719,7 +719,7 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session); * * Add a host and its associated key to the collection of known hosts. * - * The 'type' argument specifies on what format the given host is: + * The 'type' argument specifies on what format the given host and keys are: * * plain - ascii "hostname.domain.tld" * sha1 - SHA1( ) base64-encoded! @@ -732,6 +732,8 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session); * a custom type is used, salt is ignored and you must provide the host * pre-hashed when checking for it in the libssh2_knownhost_check() function. * + * The keylen parameter may be ommitted (zero) if the key is provided as a + * NULL-terminated base64-encoded string. */ /* host format (2 bits) */ @@ -759,6 +761,41 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, const char *key, size_t keylen, int typemask, struct libssh2_knownhost **store); +/* + * libssh2_knownhost_addc + * + * Add a host and its associated key to the collection of known hosts. + * + * Takes a comment argument that may be NULL. A NULL comment indicates + * there is no comment and the entry will end directly after the key + * when written out to a file. An empty string "" comment will indicate an + * empty comment which will cause a single space to be written after the key. + * + * The 'type' argument specifies on what format the given host and keys are: + * + * plain - ascii "hostname.domain.tld" + * sha1 - SHA1( ) base64-encoded! + * custom - another hash + * + * If 'sha1' is selected as type, the salt must be provided to the salt + * argument. This too base64 encoded. + * + * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If + * a custom type is used, salt is ignored and you must provide the host + * pre-hashed when checking for it in the libssh2_knownhost_check() function. + * + * The keylen parameter may be ommitted (zero) if the key is provided as a + * NULL-terminated base64-encoded string. + */ + +LIBSSH2_API int +libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts, + const char *host, + const char *salt, + const char *key, size_t keylen, + const char *comment, size_t commentlen, int typemask, + struct libssh2_knownhost **store); + /* * libssh2_knownhost_check * @@ -940,8 +977,8 @@ libssh2_agent_list_identities(LIBSSH2_AGENT *agent); */ LIBSSH2_API int libssh2_agent_get_identity(LIBSSH2_AGENT *agent, - struct libssh2_agent_publickey **store, - struct libssh2_agent_publickey *prev); + struct libssh2_agent_publickey **store, + struct libssh2_agent_publickey *prev); /* * libssh2_agent_userauth() @@ -952,8 +989,8 @@ libssh2_agent_get_identity(LIBSSH2_AGENT *agent, */ LIBSSH2_API int libssh2_agent_userauth(LIBSSH2_AGENT *agent, - const char *username, - struct libssh2_agent_publickey *identity); + const char *username, + struct libssh2_agent_publickey *identity); /* * libssh2_agent_disconnect() diff --git a/src/knownhost.c b/src/knownhost.c index fb0bff5..cd28741 100644 --- a/src/knownhost.c +++ b/src/knownhost.c @@ -48,6 +48,7 @@ struct known_host { size_t salt_len; /* size of salt */ char *key; /* the (allocated) associated key. This is kept base64 encoded in memory. */ + char *comment; /* the (allocated) optional comment text, may be NULL */ /* this is the struct we expose externally */ struct libssh2_knownhost external; @@ -62,6 +63,8 @@ struct _LIBSSH2_KNOWNHOSTS static void free_host(LIBSSH2_SESSION *session, struct known_host *entry) { if(entry) { + if(entry->comment) + LIBSSH2_FREE(session, entry->comment); if(entry->key) LIBSSH2_FREE(session, entry->key); if(entry->salt) @@ -119,31 +122,12 @@ static struct libssh2_knownhost *knownhost_to_external(struct known_host *node) return ext; } -/* - * libssh2_knownhost_add - * - * Add a host and its associated key to the collection of known hosts. - * - * The 'type' argument specifies on what format the given host and keys are: - * - * plain - ascii "hostname.domain.tld" - * sha1 - SHA1( ) base64-encoded! - * custom - another hash - * - * If 'sha1' is selected as type, the salt must be provided to the salt - * argument. This too base64 encoded. - * - * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If - * a custom type is used, salt is ignored and you must provide the host - * pre-hashed when checking for it in the libssh2_knownhost_check() function. - * - */ - -LIBSSH2_API int -libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, - const char *host, const char *salt, - const char *key, size_t keylen, - int typemask, struct libssh2_knownhost **store) +static int +knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, + const char *host, const char *salt, + const char *key, size_t keylen, + const char *comment, size_t commentlen, + int typemask, struct libssh2_knownhost **store) { struct known_host *entry = LIBSSH2_ALLOC(hosts->session, sizeof(struct known_host)); @@ -227,6 +211,20 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, entry->key = ptr; } + if (comment) { + entry->comment = LIBSSH2_ALLOC(hosts->session, commentlen+1); + if(!entry->comment) { + rc = libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for comment"); + goto error; + } + memcpy(entry->comment, comment, commentlen+1); + entry->comment[commentlen]=0; /* force a terminating zero trailer */ + } + else { + entry->comment = NULL; + } + /* add this new host to the big list of known hosts */ _libssh2_list_add(&hosts->head, &entry->node); @@ -239,6 +237,77 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, return rc; } +/* + * libssh2_knownhost_add + * + * Add a host and its associated key to the collection of known hosts. + * + * The 'type' argument specifies on what format the given host and keys are: + * + * plain - ascii "hostname.domain.tld" + * sha1 - SHA1( ) base64-encoded! + * custom - another hash + * + * If 'sha1' is selected as type, the salt must be provided to the salt + * argument. This too base64 encoded. + * + * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If + * a custom type is used, salt is ignored and you must provide the host + * pre-hashed when checking for it in the libssh2_knownhost_check() function. + * + * The keylen parameter may be ommitted (zero) if the key is provided as a + * NULL-terminated base64-encoded string. + */ + +LIBSSH2_API int +libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, + const char *host, const char *salt, + const char *key, size_t keylen, + int typemask, struct libssh2_knownhost **store) +{ + return knownhost_add(hosts, host, salt, key, keylen, NULL, 0, typemask, + store); +} + + +/* + * libssh2_knownhost_addc + * + * Add a host and its associated key to the collection of known hosts. + * + * Takes a comment argument that may be NULL. A NULL comment indicates + * there is no comment and the entry will end directly after the key + * when written out to a file. An empty string "" comment will indicate an + * empty comment which will cause a single space to be written after the key. + * + * The 'type' argument specifies on what format the given host and keys are: + * + * plain - ascii "hostname.domain.tld" + * sha1 - SHA1( ) base64-encoded! + * custom - another hash + * + * If 'sha1' is selected as type, the salt must be provided to the salt + * argument. This too base64 encoded. + * + * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If + * a custom type is used, salt is ignored and you must provide the host + * pre-hashed when checking for it in the libssh2_knownhost_check() function. + * + * The keylen parameter may be ommitted (zero) if the key is provided as a + * NULL-terminated base64-encoded string. + */ + +LIBSSH2_API int +libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts, + const char *host, const char *salt, + const char *key, size_t keylen, + const char *comment, size_t commentlen, + int typemask, struct libssh2_knownhost **store) +{ + return knownhost_add(hosts, host, salt, key, keylen, comment, commentlen, + typemask, store); +} + /* * libssh2_knownhost_check * @@ -414,6 +483,10 @@ libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts) * * Parse a single known_host line pre-split into host and key. * + * The key part may include an optional comment which will be parsed here + * for ssh-rsa and ssh-dsa keys. Comments in other key types aren't handled. + * + * The function assumes new-lines have already been removed from the arguments. */ static int hostline(LIBSSH2_KNOWNHOSTS *hosts, const char *host, size_t hostlen, @@ -422,6 +495,8 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts, const char *p; const char *orig = host; const char *salt = NULL; + const char *comment = NULL; + size_t commentlen = 0; int rc; int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN; const char *sep = NULL; @@ -526,6 +601,30 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts, key++; keylen--; } + + comment = key; + commentlen = keylen; + + /* move over key */ + while(commentlen && *comment && + (*comment != ' ') && (*comment != '\t')) { + comment++; + commentlen--; + } + + /* reduce key by comment length */ + keylen -= commentlen; + + /* Distinguish empty comment (a space) from no comment (no space) */ + if (commentlen == 0) + comment = NULL; + + /* skip whitespaces */ + while(commentlen && *comment && + ((*comment ==' ') || (*comment == '\t'))) { + comment++; + commentlen--; + } break; default: /* unknown key format */ @@ -540,9 +639,10 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts, memcpy(hostbuf, sep, seplen); hostbuf[seplen]=0; - rc = libssh2_knownhost_add(hosts, hostbuf, salt, key, keylen, - type | LIBSSH2_KNOWNHOST_KEYENC_BASE64, - NULL); + rc = libssh2_knownhost_addc(hosts, hostbuf, salt, key, keylen, + comment, commentlen, + type | LIBSSH2_KNOWNHOST_KEYENC_BASE64, + NULL); if(rc) return rc; } @@ -552,9 +652,10 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts, memcpy(hostbuf, host, hostlen); hostbuf[hostlen]=0; - rc = libssh2_knownhost_add(hosts, hostbuf, salt, key, keylen, - type | LIBSSH2_KNOWNHOST_KEYENC_BASE64, - NULL); + rc = libssh2_knownhost_addc(hosts, hostbuf, salt, key, keylen, comment, + commentlen, + type | LIBSSH2_KNOWNHOST_KEYENC_BASE64, + NULL); return rc; } @@ -724,6 +825,7 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, }; const char *keytype; size_t nlen; + size_t commentlen = 0; /* we only support this single file type for now, bail out on all other attempts */ @@ -739,6 +841,10 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, /* set the string used in the file */ keytype = keytypes[tindex]; + /* calculate extra space needed for comment */ + if(node->comment) + commentlen = strlen(node->comment) + 1; + if((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) == LIBSSH2_KNOWNHOST_TYPE_SHA1) { char *namealloc; @@ -761,11 +867,16 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, } nlen = strlen(saltalloc) + strlen(namealloc) + strlen(keytype) + - strlen(node->key) + 7; /* |1| + | + ' ' + \n + \0 = 7 */ + strlen(node->key) + commentlen + 7; + /* |1| + | + ' ' + \n + \0 = 7 */ if(nlen <= buflen) - sprintf(buf, "|1|%s|%s%s %s\n", saltalloc, namealloc, keytype, - node->key); + if(node->comment) + sprintf(buf, "|1|%s|%s%s %s %s\n", saltalloc, namealloc, + keytype, node->key, node->comment); + else + sprintf(buf, "|1|%s|%s%s %s\n", saltalloc, namealloc, + keytype, node->key); else rc = libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, "Known-host write buffer too small"); @@ -774,11 +885,16 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts, free(saltalloc); } else { - nlen = strlen(node->name) + strlen(keytype) + strlen(node->key) + 3; + nlen = strlen(node->name) + strlen(keytype) + strlen(node->key) + + commentlen + 3; /* ' ' + '\n' + \0 = 3 */ if(nlen <= buflen) /* these types have the plain name */ - sprintf(buf, "%s%s %s\n", node->name, keytype, node->key); + if(node->comment) + sprintf(buf, "%s%s %s %s\n", node->name, keytype, node->key, + node->comment); + else + sprintf(buf, "%s%s %s\n", node->name, keytype, node->key); else rc = libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, "Known-host write buffer too small");