/* mac_method_hmac_sha256_hash * Calculate hash using full sha256 value */ static int mac_method_hmac_sha2_256_hash(LIBSSH2_SESSION * session, unsigned char *buf, uint32_t seqno, const unsigned char *packet, uint32_t packet_len, const unsigned char *addtl, uint32_t addtl_len, void **abstract) { libssh2_hmac_ctx ctx; unsigned char seqno_buf[4]; (void) session; _libssh2_htonu32(seqno_buf, seqno); libssh2_hmac_ctx_init(ctx); libssh2_hmac_sha256_init(&ctx, *abstract, 32); libssh2_hmac_update(ctx, seqno_buf, 4); libssh2_hmac_update(ctx, packet, packet_len); if(addtl && addtl_len) { libssh2_hmac_update(ctx, addtl, addtl_len); } libssh2_hmac_final(ctx, buf); libssh2_hmac_cleanup(&ctx); return 0; }
/* * libssh2_knownhost_check * * Check a host and its associated key against the collection of known hosts. * * The typemask is the type/format of the given host name and key * * plain - ascii "hostname.domain.tld" * sha1 - NOT SUPPORTED AS INPUT * custom - prehashed base64 encoded. Note that this cannot use any salts. * * Returns: * * LIBSSH2_KNOWNHOST_CHECK_FAILURE * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND * LIBSSH2_KNOWNHOST_CHECK_MATCH * LIBSSH2_KNOWNHOST_CHECK_MISMATCH */ LIBSSH2_API int libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts, const char *host, const char *key, size_t keylen, int typemask, struct libssh2_knownhost **ext) { struct known_host *node = _libssh2_list_first(&hosts->head); struct known_host *badkey = NULL; int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK; char *keyalloc = NULL; int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND; if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1) /* we can't work with a sha1 as given input */ return LIBSSH2_KNOWNHOST_CHECK_MISMATCH; if(!(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64)) { /* we got a raw key input, convert it to base64 for the checks below */ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen, &keyalloc); if(!nlen) return LIBSSH2_KNOWNHOST_CHECK_FAILURE; /* make the key point to this */ key = keyalloc; keylen = nlen; } while (node) { int match = 0; switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) { case LIBSSH2_KNOWNHOST_TYPE_PLAIN: if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) match = !strcmp(host, node->name); break; case LIBSSH2_KNOWNHOST_TYPE_CUSTOM: if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM) match = !strcmp(host, node->name); break; case LIBSSH2_KNOWNHOST_TYPE_SHA1: if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) { /* when we have the sha1 version stored, we can use a plain input to produce a hash to compare with the stored hash. */ libssh2_hmac_ctx ctx; unsigned char hash[SHA_DIGEST_LENGTH]; if(SHA_DIGEST_LENGTH != node->name_len) { /* the name hash length must be the sha1 size or we can't match it */ break; } libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len); libssh2_hmac_update(ctx, (unsigned char *)host, strlen(host)); libssh2_hmac_final(ctx, hash); libssh2_hmac_cleanup(&ctx); if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH)) /* this is a node we're interested in */ match = 1; } break; default: /* unsupported type */ break; } if(match) { /* host name match, now compare the keys */ if(!strcmp(key, node->key)) { /* they match! */ *ext = knownhost_to_external(node); badkey = NULL; rc = LIBSSH2_KNOWNHOST_CHECK_MATCH; break; } else { /* remember the first node that had a host match but a failed key match since we continue our search from here */ if(!badkey) badkey = node; } } node= _libssh2_list_next(&node->node); } if(badkey) { /* key mismatch */ *ext = knownhost_to_external(badkey); rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH; } if(keyalloc) LIBSSH2_FREE(hosts->session, keyalloc); return rc; }
/* * knownhost_check * * Check a host and its associated key against the collection of known hosts. * * The typemask is the type/format of the given host name and key * * plain - ascii "hostname.domain.tld" * sha1 - NOT SUPPORTED AS INPUT * custom - prehashed base64 encoded. Note that this cannot use any salts. * * Returns: * * LIBSSH2_KNOWNHOST_CHECK_FAILURE * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND * LIBSSH2_KNOWNHOST_CHECK_MATCH * LIBSSH2_KNOWNHOST_CHECK_MISMATCH */ static int knownhost_check(LIBSSH2_KNOWNHOSTS *hosts, const char *hostp, int port, const char *key, size_t keylen, int typemask, struct libssh2_knownhost **ext) { struct known_host *node; struct known_host *badkey = NULL; int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK; char *keyalloc = NULL; int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND; char hostbuff[270]; /* most host names can't be longer than like 256 */ const char *host; int numcheck; /* number of host combos to check */ int match = 0; if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1) /* we can't work with a sha1 as given input */ return LIBSSH2_KNOWNHOST_CHECK_MISMATCH; /* if a port number is given, check for a '[host]:port' first before the plain 'host' */ if(port >= 0) { int len = snprintf(hostbuff, sizeof(hostbuff), "[%s]:%d", hostp, port); if (len < 0 || len >= (int)sizeof(hostbuff)) { _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL, "Known-host write buffer too small"); return LIBSSH2_KNOWNHOST_CHECK_FAILURE; } host = hostbuff; numcheck = 2; /* check both combos, start with this */ } else { host = hostp; numcheck = 1; /* only check this host version */ } if(!(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64)) { /* we got a raw key input, convert it to base64 for the checks below */ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen, &keyalloc); if(!nlen) { _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for base64-encoded " "key"); return LIBSSH2_KNOWNHOST_CHECK_FAILURE; } /* make the key point to this */ key = keyalloc; } do { node = _libssh2_list_first(&hosts->head); while (node) { switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) { case LIBSSH2_KNOWNHOST_TYPE_PLAIN: if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) match = !strcmp(host, node->name); break; case LIBSSH2_KNOWNHOST_TYPE_CUSTOM: if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM) match = !strcmp(host, node->name); break; case LIBSSH2_KNOWNHOST_TYPE_SHA1: if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) { /* when we have the sha1 version stored, we can use a plain input to produce a hash to compare with the stored hash. */ libssh2_hmac_ctx ctx; unsigned char hash[SHA_DIGEST_LENGTH]; if(SHA_DIGEST_LENGTH != node->name_len) { /* the name hash length must be the sha1 size or we can't match it */ break; } libssh2_hmac_sha1_init(&ctx, (unsigned char *)node->salt, node->salt_len); libssh2_hmac_update(ctx, (unsigned char *)host, strlen(host)); libssh2_hmac_final(ctx, hash); libssh2_hmac_cleanup(&ctx); if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH)) /* this is a node we're interested in */ match = 1; } break; default: /* unsupported type */ break; } if(match) { int host_key_type = typemask & LIBSSH2_KNOWNHOST_KEY_MASK; int known_key_type = node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK; /* match on key type as follows: - never match on an unknown key type - if key_type is set to zero, ignore it an match always - otherwise match when both key types are equal */ if ( (host_key_type != LIBSSH2_KNOWNHOST_KEY_UNKNOWN ) && ( (host_key_type == 0) || (host_key_type == known_key_type) ) ) { /* host name and key type match, now compare the keys */ if(!strcmp(key, node->key)) { /* they match! */ if (ext) *ext = knownhost_to_external(node); badkey = NULL; rc = LIBSSH2_KNOWNHOST_CHECK_MATCH; break; } else { /* remember the first node that had a host match but a failed key match since we continue our search from here */ if(!badkey) badkey = node; } } match = 0; /* don't count this as a match anymore */ } node= _libssh2_list_next(&node->node); } host = hostp; } while(!match && --numcheck); if(badkey) { /* key mismatch */ if (ext) *ext = knownhost_to_external(badkey); rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH; } if(keyalloc) LIBSSH2_FREE(hosts->session, keyalloc); return rc; }