int sftp_cipher_write_data(struct ssh2_packet *pkt, unsigned char *buf, size_t *buflen) { struct sftp_cipher *cipher; EVP_CIPHER_CTX *cipher_ctx; cipher = &(write_ciphers[write_cipher_idx]); cipher_ctx = write_ctxs[write_cipher_idx]; if (cipher->key) { int res; unsigned char *data, *ptr; uint32_t datalen, datasz = sizeof(uint32_t) + pkt->packet_len; datalen = datasz; ptr = data = palloc(pkt->pool, datasz); sftp_msg_write_int(&data, &datalen, pkt->packet_len); sftp_msg_write_byte(&data, &datalen, pkt->padding_len); sftp_msg_write_data(&data, &datalen, pkt->payload, pkt->payload_len, FALSE); sftp_msg_write_data(&data, &datalen, pkt->padding, pkt->padding_len, FALSE); res = EVP_Cipher(cipher_ctx, buf, ptr, (datasz - datalen)); if (res != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error encrypting %s data for client: %s", cipher->algo, sftp_crypto_get_errors()); errno = EIO; return -1; } *buflen = (datasz - datalen); #ifdef SFTP_DEBUG_PACKET { unsigned int i; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "encrypted packet data (len %lu):", (unsigned long) *buflen); for (i = 0; i < *buflen;) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, " %02x%02x %02x%02x %02x%02x %02x%02x", ((unsigned char *) buf)[i], ((unsigned char *) buf)[i+1], ((unsigned char *) buf)[i+2], ((unsigned char *) buf)[i+3], ((unsigned char *) buf)[i+4], ((unsigned char *) buf)[i+5], ((unsigned char *) buf)[i+6], ((unsigned char *) buf)[i+7]); i += 8; } } #endif return 0; } *buflen = 0; return 0; }
int sftp_kbdint_send_challenge(const char *user, const char *instruction, unsigned int count, sftp_kbdint_challenge_t *challenges) { register unsigned int i; unsigned char *buf, *ptr; uint32_t buflen, bufsz; struct ssh2_packet *pkt; int res; if (count == 0 || challenges == NULL) { errno = EINVAL; return -1; } pkt = sftp_ssh2_packet_create(kbdint_pool); /* XXX Is this large enough? Too large? */ buflen = bufsz = 3072; buf = ptr = palloc(pkt->pool, bufsz); /* See RFC4256, Section 3.2. */ sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_USER_AUTH_INFO_REQ); if (user) { sftp_msg_write_string(&buf, &buflen, sftp_utf8_encode_str(pkt->pool, user)); } else { /* Empty user strings are allowed. */ sftp_msg_write_string(&buf, &buflen, ""); } if (instruction) { sftp_msg_write_string(&buf, &buflen, sftp_utf8_encode_str(pkt->pool, instruction)); } else { /* Empty instruction strings are allowed. */ sftp_msg_write_string(&buf, &buflen, ""); } /* Empty language string. */ sftp_msg_write_string(&buf, &buflen, ""); sftp_msg_write_int(&buf, &buflen, count); for (i = 0; i < count; i++) { sftp_msg_write_string(&buf, &buflen, challenges[i].challenge); sftp_msg_write_bool(&buf, &buflen, challenges[i].display_response); } pkt->payload = ptr; pkt->payload_len = (bufsz - buflen); pr_trace_msg(trace_channel, 9, "sending USER_AUTH_INFO_REQ message to client"); res = sftp_ssh2_packet_write(sftp_conn->wfd, pkt); destroy_pool(pkt->pool); return res; }
void sftp_disconnect_send(uint32_t reason, const char *explain, const char *file, int lineno, const char *func) { struct ssh2_packet *pkt; const pr_netaddr_t *remote_addr; const char *lang = "en-US"; unsigned char *buf, *ptr; uint32_t buflen, bufsz; int sockfd; /* Send the client a DISCONNECT mesg. */ pkt = sftp_ssh2_packet_create(sftp_pool); remote_addr = pr_netaddr_get_sess_remote_addr(); buflen = bufsz = 1024; ptr = buf = palloc(pkt->pool, bufsz); if (explain == NULL) { register unsigned int i; for (i = 0; explanations[i].explain; i++) { if (explanations[i].code == reason) { explain = explanations[i].explain; lang = explanations[i].lang; if (lang == NULL) { lang = "en-US"; } break; } } if (explain == NULL) { explain = "Unknown reason"; } } else { lang = "en-US"; } if (strlen(func) > 0) { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d:%s()]", explain, file, lineno, func); } else { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d]", explain, file, lineno); } sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_DISCONNECT); sftp_msg_write_int(&buf, &buflen, reason); sftp_msg_write_string(&buf, &buflen, explain); sftp_msg_write_string(&buf, &buflen, lang); pkt->payload = ptr; pkt->payload_len = (bufsz - buflen); (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "disconnecting %s (%s)", pr_netaddr_get_ipstr(remote_addr), explain); /* If we are called very early in the connection lifetime, then the * sftp_conn variable may not have been set yet, thus the conditional here. */ if (sftp_conn != NULL) { sockfd = sftp_conn->wfd; } else { sockfd = session.c->wfd; } /* Explicitly set a short poll timeout of 5 secs. */ sftp_ssh2_packet_set_poll_timeout(5); if (sftp_ssh2_packet_write(sockfd, pkt) < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 12, "error writing DISCONNECT message: %s", strerror(xerrno)); } destroy_pool(pkt->pool); }
int sftp_mac_write_data(struct ssh2_packet *pkt) { struct sftp_mac *mac; HMAC_CTX *mac_ctx; mac = &(write_macs[write_mac_idx]); mac_ctx = &(write_ctxs[write_mac_idx]); if (mac->key) { unsigned char *mac_data; char *buf, *ptr; uint32_t buflen, bufsz = (sizeof(uint32_t) * 2) + pkt->packet_len, mac_len = 0; mac_data = pcalloc(pkt->pool, EVP_MAX_MD_SIZE); buflen = bufsz; ptr = buf = sftp_msg_getbuf(pkt->pool, bufsz); sftp_msg_write_int(&buf, &buflen, pkt->seqno); sftp_msg_write_int(&buf, &buflen, pkt->packet_len); sftp_msg_write_byte(&buf, &buflen, pkt->padding_len); sftp_msg_write_data(&buf, &buflen, pkt->payload, pkt->payload_len, FALSE); sftp_msg_write_data(&buf, &buflen, pkt->padding, pkt->padding_len, FALSE); #if OPENSSL_VERSION_NUMBER > 0x000907000L # if OPENSSL_VERSION_NUMBER >= 0x10000001L if (HMAC_Init_ex(mac_ctx, NULL, 0, NULL, NULL) != 1) { pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error resetting HMAC context: %s", sftp_crypto_get_errors()); errno = EPERM; return -1; } # else HMAC_Init_ex(mac_ctx, NULL, 0, NULL, NULL); # endif /* OpenSSL-1.0.0 and later */ #else HMAC_Init(mac_ctx, NULL, 0, NULL); #endif /* OpenSSL-0.9.7 and later */ #if OPENSSL_VERSION_NUMBER >= 0x10000001L if (HMAC_Update(mac_ctx, (unsigned char *) ptr, (bufsz - buflen)) != 1) { pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error adding %lu bytes of data to HMAC context: %s", (unsigned long) (bufsz - buflen), sftp_crypto_get_errors()); errno = EPERM; return -1; } if (HMAC_Final(mac_ctx, mac_data, &mac_len) != 1) { pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error finalizing HMAC context: %s", sftp_crypto_get_errors()); errno = EPERM; return -1; } #else HMAC_Update(mac_ctx, (unsigned char *) ptr, (bufsz - buflen)); HMAC_Final(mac_ctx, mac_data, &mac_len); #endif /* OpenSSL-1.0.0 and later */ if (mac_len == 0) { pkt->mac = NULL; pkt->mac_len = 0; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error computing MAC using %s: %s", mac->algo, sftp_crypto_get_errors()); return -1; } if (mac->mac_len != 0) { mac_len = mac->mac_len; } pkt->mac_len = mac_len; pkt->mac = pcalloc(pkt->pool, pkt->mac_len); memcpy(pkt->mac, mac_data, mac_len); #ifdef SFTP_DEBUG_PACKET { unsigned int i; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "server MAC (len %lu, seqno %lu):", (unsigned long) pkt->mac_len, (unsigned long) pkt->seqno); for (i = 0; i < mac_len;) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, " %02x%02x %02x%02x %02x%02x %02x%02x", ((unsigned char *) pkt->mac)[i], ((unsigned char *) pkt->mac)[i+1], ((unsigned char *) pkt->mac)[i+2], ((unsigned char *) pkt->mac)[i+3], ((unsigned char *) pkt->mac)[i+4], ((unsigned char *) pkt->mac)[i+5], ((unsigned char *) pkt->mac)[i+6], ((unsigned char *) pkt->mac)[i+7]); i += 8; } } #endif return 0; } pkt->mac = NULL; pkt->mac_len = 0; return 0; }
static unsigned char *agent_request(pool *p, int fd, const char *path, unsigned char *req, uint32_t reqlen, uint32_t *resplen) { unsigned char msg[AGENT_REQUEST_MSGSZ], *buf, *ptr; uint32_t bufsz, buflen; int res; bufsz = buflen = sizeof(msg); buf = ptr = msg; sftp_msg_write_int(&buf, &buflen, reqlen); /* Send the message length to the agent. */ res = write(fd, ptr, (bufsz - buflen)); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 3, "error sending request length to SSH agent at '%s': %s", path, strerror(xerrno)); errno = xerrno; return NULL; } /* Handle short writes. */ if (res != (bufsz - buflen)) { pr_trace_msg(trace_channel, 3, "short write (%d of %lu bytes sent) when talking to SSH agent at '%s'", res, (unsigned long) (bufsz - buflen), path); errno = EIO; return NULL; } /* Send the message payload to the agent. */ res = write(fd, req, reqlen); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 3, "error sending request payload to SSH agent at '%s': %s", path, strerror(xerrno)); errno = xerrno; return NULL; } /* Handle short writes. */ if (res != reqlen) { pr_trace_msg(trace_channel, 3, "short write (%d of %lu bytes sent) when talking to SSH agent at '%s'", res, (unsigned long) reqlen, path); errno = EIO; return NULL; } /* Wait for a response from the server. */ /* XXX This needs a timeout, prevent a blocked/bad agent from stalling * the server. Maybe just set an internal timer? */ res = read(fd, msg, sizeof(uint32_t)); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 3, "error reading response length from SSH agent at '%s': %s", path, strerror(xerrno)); errno = xerrno; return NULL; } /* Sanity check the returned length; we could be dealing with a buggy * client (or something else is injecting data into the Unix domain socket). * Best be conservative: if we get a response length of more than 256KB, * it's too big. (What about very long lists of keys, and/or large keys?) */ if (res > AGENT_REPLY_MAXSZ) { pr_trace_msg(trace_channel, 1, "response length (%d) from SSH agent at '%s' exceeds maximum (%lu), " "ignoring", res, path, (unsigned long) AGENT_REPLY_MAXSZ); errno = EIO; return NULL; } buf = msg; buflen = res; *resplen = sftp_msg_read_int(p, &buf, &buflen); bufsz = buflen = *resplen; buf = ptr = palloc(p, bufsz); buflen = 0; while (buflen != *resplen) { pr_signals_handle(); res = read(fd, buf + buflen, bufsz - buflen); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 3, "error reading %d bytes of response payload from SSH agent at '%s': %s", (bufsz - buflen), path, strerror(xerrno)); errno = xerrno; return NULL; } /* XXX Handle short reads? */ buflen += res; } return ptr; }
const unsigned char *sftp_agent_sign_data(pool *p, const char *agent_path, const unsigned char *key_data, uint32_t key_datalen, const unsigned char *data, uint32_t datalen, uint32_t *sig_datalen) { int fd; unsigned char *buf, *req, *resp, *sig_data; uint32_t buflen, flags, reqlen, reqsz, resplen; char resp_status; fd = agent_connect(agent_path); if (fd < 0) { return NULL; } /* XXX When to set flags to OLD_SIGNATURE? */ flags = 0; /* Write out the request for signing the given data. */ reqsz = buflen = 1 + key_datalen + 4 + datalen + 4 + 4; req = buf = palloc(p, reqsz); sftp_msg_write_byte(&buf, &buflen, SFTP_SSH_AGENT_REQ_SIGN_DATA); sftp_msg_write_data(&buf, &buflen, key_data, key_datalen, TRUE); sftp_msg_write_data(&buf, &buflen, data, datalen, TRUE); sftp_msg_write_int(&buf, &buflen, flags); reqlen = reqsz - buflen; resp = agent_request(p, fd, agent_path, req, reqlen, &resplen); if (resp == NULL) { int xerrno = errno; (void) close(fd); errno = xerrno; return NULL; } (void) close(fd); /* Read the response from the agent. */ resp_status = sftp_msg_read_byte(p, &resp, &resplen); if (agent_failure(resp_status) == TRUE) { pr_trace_msg(trace_channel, 5, "SSH agent at '%s' indicated failure (%d) for data signing request", agent_path, resp_status); errno = EPERM; return NULL; } if (resp_status != SFTP_SSH_AGENT_RESP_SIGN_DATA) { pr_trace_msg(trace_channel, 5, "unknown response type %d from SSH agent at '%s'", resp_status, agent_path); errno = EACCES; return NULL; } *sig_datalen = sftp_msg_read_int(p, &resp, &resplen); sig_data = sftp_msg_read_data(p, &resp, &resplen, *sig_datalen); return sig_data; }