/* * _libssh2_packet_ask * * Scan the brigade for a matching packet type, optionally poll the socket for * a packet first */ int _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type, unsigned char **data, size_t *data_len, int match_ofs, const unsigned char *match_buf, size_t match_len) { LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets); _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Looking for packet of type: %d", (int) packet_type); while (packet) { if (packet->data[0] == packet_type && (packet->data_len >= (match_ofs + match_len)) && (!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) { *data = packet->data; *data_len = packet->data_len; /* unlink struct from session->packets */ _libssh2_list_remove(&packet->node); LIBSSH2_FREE(session, packet); return 0; } packet = _libssh2_list_next(&packet->node); } return -1; }
/* * libssh2_knownhost_get() * * Traverse the internal list of known hosts. Pass NULL to 'prev' to get * the first one. * * Returns: * 0 if a fine host was stored in 'store' * 1 if end of hosts * [negative] on errors */ LIBSSH2_API int libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts, struct libssh2_knownhost **ext, struct libssh2_knownhost *oprev) { struct known_host *node; if(oprev && oprev->node) { /* we have a starting point */ struct known_host *prev = oprev->node; /* get the next node in the list */ node = _libssh2_list_next(&prev->node); } else node = _libssh2_list_first(&hosts->head); if(!node) /* no (more) node */ return 1; *ext = knownhost_to_external(node); return 0; }
/* * libssh2_knownhost_free * * Free an entire collection of known hosts. * */ LIBSSH2_API void libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts) { struct known_host *node; struct known_host *next; for(node = _libssh2_list_first(&hosts->head); node; node = next) { next = _libssh2_list_next(&node->node); free_host(hosts->session, node); } LIBSSH2_FREE(hosts->session, hosts); }
static void agent_free_identities(LIBSSH2_AGENT *agent) { struct agent_publickey *node; struct agent_publickey *next; for (node = _libssh2_list_first(&agent->head); node; node = next) { next = _libssh2_list_next(&node->node); LIBSSH2_FREE(agent->session, node->external.blob); LIBSSH2_FREE(agent->session, node->external.comment); LIBSSH2_FREE(agent->session, node); } _libssh2_list_init(&agent->head); }
/* * libssh2_knownhost_writefile() * * Write hosts+key pairs to the given file. */ LIBSSH2_API int libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts, const char *filename, int type) { struct known_host *node; FILE *file; int rc = LIBSSH2_ERROR_NONE; char buffer[2048]; /* we only support this single file type for now, bail out on all other attempts */ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) return _libssh2_error(hosts->session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, "Unsupported type of known-host information " "store"); file = fopen(filename, "w"); if(!file) return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE, "Failed to open file"); for(node = _libssh2_list_first(&hosts->head); node; node = _libssh2_list_next(&node->node)) { size_t wrote = 0; size_t nwrote; rc = knownhost_writeline(hosts, node, buffer, sizeof(buffer), &wrote, type); if(rc) break; nwrote = fwrite(buffer, 1, wrote, file); if(nwrote != wrote) { /* failed to write the whole thing, bail out */ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE, "Write failed"); break; } } fclose(file); return rc; }
/* * libssh2_agent_get_identity() * * Traverse the internal list of public keys. Pass NULL to 'prev' to get * the first one. Or pass a poiner to the previously returned one to get the * next. * * Returns: * 0 if a fine public key was stored in 'store' * 1 if end of public keys * [negative] on errors */ LIBSSH2_API int libssh2_agent_get_identity(LIBSSH2_AGENT *agent, struct libssh2_agent_publickey **ext, struct libssh2_agent_publickey *oprev) { struct agent_publickey *node; if (oprev && oprev->node) { /* we have a starting point */ struct agent_publickey *prev = oprev->node; /* get the next node in the list */ node = _libssh2_list_next(&prev->node); } else node = _libssh2_list_first(&agent->head); if (!node) /* no (more) node */ return 1; *ext = agent_publickey_to_external(node); 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; }
/* * libssh2_packet_queue_listener * * Queue a connection request for a listener */ static inline int packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, unsigned long datalen, packet_queue_listener_state_t *listen_state) { /* * Look for a matching listener */ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1); unsigned char *p; LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners); char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; int rc; (void) datalen; if (listen_state->state == libssh2_NB_state_idle) { unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; listen_state->sender_channel = _libssh2_ntohu32(s); s += 4; listen_state->initial_window_size = _libssh2_ntohu32(s); s += 4; listen_state->packet_size = _libssh2_ntohu32(s); s += 4; listen_state->host_len = _libssh2_ntohu32(s); s += 4; listen_state->host = s; s += listen_state->host_len; listen_state->port = _libssh2_ntohu32(s); s += 4; listen_state->shost_len = _libssh2_ntohu32(s); s += 4; listen_state->shost = s; s += listen_state->shost_len; listen_state->sport = _libssh2_ntohu32(s); _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Remote received connection from %s:%ld to %s:%ld", listen_state->shost, listen_state->sport, listen_state->host, listen_state->port); listen_state->state = libssh2_NB_state_allocated; } if (listen_state->state != libssh2_NB_state_sent) { while (listn) { if ((listn->port == (int) listen_state->port) && (strlen(listn->host) == listen_state->host_len) && (memcmp (listn->host, listen_state->host, listen_state->host_len) == 0)) { /* This is our listener */ LIBSSH2_CHANNEL *channel = NULL; listen_state->channel = NULL; if (listen_state->state == libssh2_NB_state_allocated) { if (listn->queue_maxsize && (listn->queue_maxsize <= listn->queue_size)) { /* Queue is full */ failure_code = SSH_OPEN_RESOURCE_SHORTAGE; _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Listener queue full, ignoring"); listen_state->state = libssh2_NB_state_sent; break; } channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL)); if (!channel) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for " "new connection"); failure_code = SSH_OPEN_RESOURCE_SHORTAGE; listen_state->state = libssh2_NB_state_sent; break; } listen_state->channel = channel; channel->session = session; channel->channel_type_len = sizeof("forwarded-tcpip") - 1; channel->channel_type = LIBSSH2_ALLOC(session, channel-> channel_type_len + 1); if (!channel->channel_type) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new" " connection"); LIBSSH2_FREE(session, channel); failure_code = SSH_OPEN_RESOURCE_SHORTAGE; listen_state->state = libssh2_NB_state_sent; break; } memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); channel->remote.id = listen_state->sender_channel; channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; channel->local.id = _libssh2_channel_nextid(session); channel->local.window_size_initial = listen_state->initial_window_size; channel->local.window_size = listen_state->initial_window_size; channel->local.packet_size = listen_state->packet_size; _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Connection queued: channel %lu/%lu " "win %lu/%lu packet %lu/%lu", channel->local.id, channel->remote.id, channel->local.window_size, channel->remote.window_size, channel->local.packet_size, channel->remote.packet_size); p = listen_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; _libssh2_store_u32(&p, channel->remote.id); _libssh2_store_u32(&p, channel->local.id); _libssh2_store_u32(&p, channel->remote.window_size_initial); _libssh2_store_u32(&p, channel->remote.packet_size); listen_state->state = libssh2_NB_state_created; } if (listen_state->state == libssh2_NB_state_created) { rc = _libssh2_transport_send(session, listen_state->packet, 17, NULL, 0); if (rc == LIBSSH2_ERROR_EAGAIN) return rc; else if (rc) { listen_state->state = libssh2_NB_state_idle; return _libssh2_error(session, rc, "Unable to send channel " "open confirmation"); } /* Link the channel into the end of the queue list */ if (listen_state->channel) { _libssh2_list_add(&listn->queue, &listen_state->channel->node); listn->queue_size++; } listen_state->state = libssh2_NB_state_idle; return 0; } } listn = _libssh2_list_next(&listn->node); } listen_state->state = libssh2_NB_state_sent; } /* We're not listening to you */ p = listen_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; _libssh2_store_u32(&p, listen_state->sender_channel); _libssh2_store_u32(&p, failure_code); _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1); _libssh2_htonu32(p, 0); rc = _libssh2_transport_send(session, listen_state->packet, packet_len, NULL, 0); if (rc == LIBSSH2_ERROR_EAGAIN) { return rc; } else if (rc) { listen_state->state = libssh2_NB_state_idle; return _libssh2_error(session, rc, "Unable to send open failure"); } listen_state->state = libssh2_NB_state_idle; return 0; }