/* {{{ libssh2_comp_method_zlib_comp * zlib, a compression standard for all occasions */ static int libssh2_comp_method_zlib_comp(LIBSSH2_SESSION * session, int compress, unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest, const unsigned char *src, unsigned long src_len, void **abstract) { z_stream *strm = *abstract; /* A short-term alloc of a full data chunk is better than a series of reallocs */ char *out; int out_maxlen = compress ? (src_len + 4) : (2 * src_len); int limiter = 0; /* In practice they never come smaller than this */ if (out_maxlen < 25) { out_maxlen = 25; } if (out_maxlen > (int) payload_limit) { out_maxlen = payload_limit; } strm->next_in = (unsigned char *) src; strm->avail_in = src_len; strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen); out = (char *) strm->next_out; strm->avail_out = out_maxlen; if (!strm->next_out) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate compression/decompression buffer", 0); return -1; } while (strm->avail_in) { int status; if (compress) { status = deflate(strm, Z_PARTIAL_FLUSH); } else { status = inflate(strm, Z_PARTIAL_FLUSH); } if (status != Z_OK) { libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compress/decompression failure", 0); LIBSSH2_FREE(session, out); return -1; } if (strm->avail_in) { unsigned long out_ofs = out_maxlen - strm->avail_out; char *newout; out_maxlen += compress ? (strm->avail_in + 4) : (2 * strm->avail_in); if ((out_maxlen > (int) payload_limit) && !compress && limiter++) { libssh2_error(session, LIBSSH2_ERROR_ZLIB, "Excessive growth in decompression phase", 0); LIBSSH2_FREE(session, out); return -1; } newout = LIBSSH2_REALLOC(session, out, out_maxlen); if (!newout) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to expand compress/decompression buffer", 0); LIBSSH2_FREE(session, out); return -1; } out = newout; strm->next_out = (unsigned char *) out + out_ofs; strm->avail_out += compress ? (strm->avail_in + 4) : (2 * strm->avail_in); } else while (!strm->avail_out) { /* Done with input, might be a byte or two in internal buffer during compress * Or potentially many bytes if it's a decompress */ int grow_size = compress ? 8 : 1024; char *newout; if (out_maxlen >= (int) payload_limit) { libssh2_error(session, LIBSSH2_ERROR_ZLIB, "Excessive growth in decompression phase", 0); LIBSSH2_FREE(session, out); return -1; } if (grow_size > (int) (payload_limit - out_maxlen)) { grow_size = payload_limit - out_maxlen; } out_maxlen += grow_size; strm->avail_out = grow_size; newout = LIBSSH2_REALLOC(session, out, out_maxlen); if (!newout) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to expand final compress/decompress buffer", 0); LIBSSH2_FREE(session, out); return -1; } out = newout; strm->next_out = (unsigned char *) out + out_maxlen - grow_size; if (compress) { status = deflate(strm, Z_PARTIAL_FLUSH); } else { status = inflate(strm, Z_PARTIAL_FLUSH); } if (status != Z_OK) { libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compress/decompression failure", 0); LIBSSH2_FREE(session, out); return -1; } } } *dest = (unsigned char *) out; *dest_len = out_maxlen - strm->avail_out; *free_dest = 1; return 0; }
/* {{{ libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *publickey, const char *privatekey, const char *passphrase) { LIBSSH2_HOSTKEY_METHOD *privkeyobj; void *abstract; unsigned char buf[5]; struct iovec datavec[4]; unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data; unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 }; unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { return -1; } packet_len = username_len + method_len + pubkeydata_len + 45; /* packet_type(1) + username_len(4) + servicename_len(4) + service_name(14)"ssh-connection" + authmethod_len(4) + authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) + publickey_len(4) */ /* Preallocate space for an overall length, method name again, * and the signature, which won't be any larger than the size of the publickeydata itself */ s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); *(s++) = SSH_MSG_USERAUTH_REQUEST; libssh2_htonu32(s, username_len); s += 4; memcpy(s, username, username_len); s += username_len; libssh2_htonu32(s, 14); s += 4; memcpy(s, "ssh-connection", 14); s += 14; libssh2_htonu32(s, 9); s += 4; memcpy(s, "publickey", 9); s += 9; b = s; *(s++) = 0; /* Not sending signature with *this* packet */ libssh2_htonu32(s, method_len); s += 4; memcpy(s, method, method_len); s += method_len; libssh2_htonu32(s, pubkeydata_len); s += 4; memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication"); #endif if (libssh2_packet_write(session, packet, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); return -1; } if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful"); #endif /* God help any SSH server that allows an UNVERIFIED public key to validate the user */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } if (data[0] == SSH_MSG_USERAUTH_FAILURE) { /* This public key is not allowed for this user on this server */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0); return -1; } /* Semi-Success! */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, pubkeydata); if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, (const char *)method, method_len, privatekey, passphrase)) { LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, packet); return -1; } *b = 0xFF; libssh2_htonu32(buf, session->session_id_len); datavec[0].iov_base = buf; datavec[0].iov_len = 4; datavec[1].iov_base = session->session_id; datavec[1].iov_len = session->session_id_len; datavec[2].iov_base = packet; datavec[2].iov_len = packet_len; if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, packet); if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } return -1; } if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } if (sig_len > pubkeydata_len) { /* Should *NEVER* happen, but...well.. better safe than sorry */ packet = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ if (!packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0); LIBSSH2_FREE(session, method); return -1; } } s = packet + packet_len; libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; libssh2_htonu32(s, method_len); s += 4; memcpy(s, method, method_len); s += method_len; LIBSSH2_FREE(session, method); libssh2_htonu32(s, sig_len); s += 4; memcpy(s, sig, sig_len); s += sig_len; LIBSSH2_FREE(session, sig); #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2"); #endif if (libssh2_packet_write(session, packet, s - packet)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); /* PK_OK is no longer valid */ reply_codes[2] = 0; if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful"); #endif /* We are us and we've proved it. */ LIBSSH2_FREE(session, data); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } /* This public key is not allowed for this user on this server */ LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); return -1; }
/* {{{ libssh2_publickey_list_fetch * Fetch a list of supported public key from a server */ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; libssh2_publickey_list *list = NULL; unsigned char *s, buffer[12], *data = NULL; unsigned long buffer_len = 12, keys = 0, max_keys = 0, data_len, i, response; /* packet_len(4) + list_len(4) + "list"(4) */ s = buffer; libssh2_htonu32(s, buffer_len - 4); s += 4; libssh2_htonu32(s, sizeof("list") - 1); s += 4; memcpy(s, "list", sizeof("list") - 1); s += sizeof("list") - 1; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet"); #endif if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0); return -1; } while (1) { if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); goto err_exit; } s = data; if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); goto err_exit; } switch (response) { case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: /* Error, or processing complete */ { unsigned long status, descr_len, lang_len; unsigned char *descr, *lang; status = libssh2_ntohu32(s); s += 4; descr_len = libssh2_ntohu32(s); s += 4; descr = s; s += descr_len; lang_len = libssh2_ntohu32(s); s += 4; lang = s; s += lang_len; if (s > data + data_len) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); goto err_exit; } if (status == LIBSSH2_PUBLICKEY_SUCCESS) { LIBSSH2_FREE(session, data); *pkey_list = list; *num_keys = keys; return 0; } libssh2_publickey_status_error(pkey, session, status, descr, descr_len); goto err_exit; } case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY: /* What we want */ if (keys >= max_keys) { /* Grow the key list if necessary */ max_keys += 8; list = LIBSSH2_REALLOC(session, list, (max_keys + 1) * sizeof(libssh2_publickey_list)); if (!list) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey list", 0); goto err_exit; } } if (pkey->version == 1) { unsigned long comment_len; comment_len = libssh2_ntohu32(s); s += 4; if (comment_len) { list[keys].num_attrs = 1; list[keys].attrs = LIBSSH2_ALLOC(session, sizeof(libssh2_publickey_attribute)); if (!list[keys].attrs) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); goto err_exit; } list[keys].attrs[0].name = "comment"; list[keys].attrs[0].name_len = sizeof("comment") - 1; list[keys].attrs[0].value = s; list[keys].attrs[0].value_len = comment_len; list[keys].attrs[0].mandatory = 0; s += comment_len; } else { list[keys].num_attrs = 0; list[keys].attrs = NULL; } list[keys].name_len = libssh2_ntohu32(s); s += 4; list[keys].name = s; s += list[keys].name_len; list[keys].blob_len = libssh2_ntohu32(s); s += 4; list[keys].blob = s; s += list[keys].blob_len; } else { /* Version == 2 */ list[keys].name_len = libssh2_ntohu32(s); s += 4; list[keys].name = s; s += list[keys].name_len; list[keys].blob_len = libssh2_ntohu32(s); s += 4; list[keys].blob = s; s += list[keys].blob_len; list[keys].num_attrs = libssh2_ntohu32(s); s += 4; if (list[keys].num_attrs) { list[keys].attrs = LIBSSH2_ALLOC(session, list[keys].num_attrs * sizeof(libssh2_publickey_attribute)); if (!list[keys].attrs) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); goto err_exit; } for(i = 0; i < list[keys].num_attrs; i++) { list[keys].attrs[i].name_len = libssh2_ntohu32(s); s += 4; list[keys].attrs[i].name = s; s += list[keys].attrs[i].name_len; list[keys].attrs[i].value_len = libssh2_ntohu32(s); s += 4; list[keys].attrs[i].value = s; s += list[keys].attrs[i].value_len; list[keys].attrs[i].mandatory = 0; /* actually an ignored value */ } } else { list[keys].attrs = NULL; } } list[keys].packet = data; /* To be FREEd in libssh2_publickey_list_free() */ keys++; list[keys].packet = NULL; /* Terminate the list */ data = NULL; break; default: /* Unknown/Unexpected */ libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); LIBSSH2_FREE(session, data); } } /* Only reached via explicit goto */ err_exit: if (data) { LIBSSH2_FREE(session, data); } if (list) { libssh2_publickey_list_free(pkey, list); } return -1; }
int _libssh2_pem_parse (LIBSSH2_SESSION *session, const char *headerbegin, const char *headerend, FILE *fp, char **data, unsigned int *datalen) { char line[LINE_SIZE]; char *b64data = NULL; unsigned int b64datalen = 0; int ret; do { if (readline(line, LINE_SIZE, fp)) { return -1; } } while (strcmp (line, headerbegin) != 0); *line = '\0'; do { if (*line) { char *tmp; size_t linelen; linelen = strlen (line); tmp = LIBSSH2_REALLOC (session, b64data, b64datalen + linelen); if (!tmp) { ret = -1; goto out; } memcpy (tmp + b64datalen, line, linelen); b64data = tmp; b64datalen += linelen; } if (readline(line, LINE_SIZE, fp)) { ret = -1; goto out; } } while (strcmp (line, headerend) != 0); if (libssh2_base64_decode(session, data, datalen, b64data, b64datalen)) { ret = -1; goto out; } ret = 0; out: if (b64data) { LIBSSH2_FREE (session, b64data); } return ret; }
static char * convert_ccsid(LIBSSH2_SESSION *session, libssh2_string_cache **cache, unsigned short outccsid, unsigned short inccsid, const char *instring, ssize_t inlen, size_t *outlen) { char *inp; char *outp; size_t olen; size_t ilen; size_t buflen; size_t curlen; ssize_t termsize; int i; char *dst; libssh2_string_cache *outstring; QtqCode_T incode; QtqCode_T outcode; iconv_t cd; if (!instring) { if (outlen) *outlen = 0; return NULL; } if (outlen) *outlen = -1; if (!session || !cache) return NULL; /* Get terminator size. */ termsize = terminator_size(outccsid); if (termsize < 0) return NULL; /* Prepare conversion parameters. */ memset((void *) &incode, 0, sizeof incode); memset((void *) &outcode, 0, sizeof outcode); incode.CCSID = inccsid; outcode.CCSID = outccsid; curlen = OFFSET_OF(libssh2_string_cache, string); inp = (char *) instring; ilen = inlen; buflen = inlen + curlen; if (inlen < 0) { incode.length_option = 1; buflen = STRING_GRANULE; ilen = 0; } /* Allocate output string buffer and open conversion descriptor. */ dst = LIBSSH2_ALLOC(session, buflen + termsize); if (!dst) return NULL; cd = QtqIconvOpen(&outcode, &incode); if (cd.return_value == -1) { LIBSSH2_FREE(session, (char *) dst); return NULL; } /* Convert string. */ for (;;) { outp = dst + curlen; olen = buflen - curlen; i = iconv(cd, &inp, &ilen, &outp, &olen); if (inlen < 0 && olen == buflen - curlen) { /* Special case: converted 0-length (sub)strings do not store the terminator. */ if (termsize) { memset(outp, 0, termsize); olen -= termsize; } } curlen = buflen - olen; if (i >= 0 || errno != E2BIG) break; /* Must expand buffer. */ buflen += STRING_GRANULE; outp = LIBSSH2_REALLOC(session, dst, buflen + termsize); if (!outp) break; dst = outp; } iconv_close(cd); /* Check for error. */ if (i < 0 || !outp) { LIBSSH2_FREE(session, dst); return NULL; } /* Process terminator. */ if (inlen < 0) curlen -= termsize; else if (termsize) memset(dst + curlen, 0, termsize); /* Shorten buffer if possible. */ if (curlen < buflen) dst = LIBSSH2_REALLOC(session, dst, curlen + termsize); /* Link to cache. */ outstring = (libssh2_string_cache *) dst; outstring->next = *cache; *cache = outstring; /* Return length if required. */ if (outlen) *outlen = curlen - OFFSET_OF(libssh2_string_cache, string); return outstring->string; }