int ReceiveTransaction(int sd, char *buffer, int *more) { char proto[CF_INBAND_OFFSET + 1]; char status = 'x'; unsigned int len = 0; memset(proto, 0, CF_INBAND_OFFSET + 1); if (RecvSocketStream(sd, proto, CF_INBAND_OFFSET, 0) == -1) /* Get control channel */ { return -1; } sscanf(proto, "%c %u", &status, &len); CfDebug("Transaction Receive [%s][%s]\n", proto, proto + CF_INBAND_OFFSET); if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { CfOut(cf_error, "", "Bad transaction packet -- too long (%c %d) Proto = %s ", status, len, proto); return -1; } if (strncmp(proto, "CAUTH", 5) == 0) { CfDebug("Version 1 protocol connection attempted - no you don't!!\n"); return -1; } if (more != NULL) { switch (status) { case 'm': *more = true; break; default: *more = false; } } return RecvSocketStream(sd, buffer, len, 0); }
int ReceiveTransaction(int sd, char *buffer, int *more) { char proto[CF_INBAND_OFFSET + 1]; char status = 'x'; unsigned int len = 0; memset(proto, 0, CF_INBAND_OFFSET + 1); if (RecvSocketStream(sd, proto, CF_INBAND_OFFSET) == -1) /* Get control channel */ { return -1; } sscanf(proto, "%c %u", &status, &len); if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "Bad transaction packet -- too long (%c %d) Proto = %s ", status, len, proto); return -1; } if (strncmp(proto, "CAUTH", 5) == 0) { return -1; } if (more != NULL) { switch (status) { case 'm': *more = true; break; default: *more = false; } } return RecvSocketStream(sd, buffer, len); }
/** * Receive a transaction packet of at most CF_BUFSIZE-1 bytes, and * NULL-terminate it. * * @return 0 in case of socket closed, -1 in case of other error, or * >0 the number of bytes read. */ int ReceiveTransaction(const ConnectionInfo *conn_info, char *buffer, int *more) { char proto[CF_INBAND_OFFSET + 1] = { 0 }; int ret; /* Get control channel. */ switch(conn_info->protocol) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(conn_info->sd, proto, CF_INBAND_OFFSET); break; case CF_PROTOCOL_TLS: ret = TLSRecv(conn_info->ssl, proto, CF_INBAND_OFFSET); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", conn_info->protocol); ret = -1; } if (ret == -1 || ret == 0) { return ret; } else if (ret != CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: bogus short header (%d bytes: '%s')", ret, proto); return -1; } LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction header: ", proto, ret); char status = 'x'; int len = 0; ret = sscanf(proto, "%c %d", &status, &len); if (ret != 2) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: bogus header: %s", proto); return -1; } if (status != CF_MORE && status != CF_DONE) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: bogus header (more='%c')", status); return -1; } if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: packet too long (len=%d)", len); return -1; } else if (len <= 0) { /* Zero-length packets are disallowed, because * ReceiveTransaction() == 0 currently means connection closed. */ Log(LOG_LEVEL_ERR, "ReceiveTransaction: packet too short (len=%d)", len); return -1; } if (more != NULL) { switch (status) { case CF_MORE: *more = true; break; case CF_DONE: *more = false; break; default: ProgrammingError("Unreachable, " "bogus headers have already been checked!"); } } /* Get data. */ switch(conn_info->protocol) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(conn_info->sd, buffer, len); break; case CF_PROTOCOL_TLS: ret = TLSRecv(conn_info->ssl, buffer, len); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", conn_info->protocol); ret = -1; } if (ret == -1 || ret == 0) { return ret; } else if (ret != len) { /* Should never happen given that we are using SSL_MODE_AUTO_RETRY and * that transaction payload < CF_BUFSIZE < TLS record size. */ Log(LOG_LEVEL_ERR, "Partial transaction read %d != %d bytes!", ret, len); return -1; } LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction data: ", buffer, ret); return ret; }
/** * @return 0 in case of socket closed, -1 in case of other error, or * >0 the number of bytes read. */ int ReceiveTransaction(const ConnectionInfo *conn_info, char *buffer, int *more) { char proto[CF_INBAND_OFFSET + 1] = { 0 }; char status = 'x'; unsigned int len = 0; int ret; /* Get control channel. */ switch(ConnectionInfoProtocolVersion(conn_info)) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(ConnectionInfoSocket(conn_info), proto, CF_INBAND_OFFSET); break; case CF_PROTOCOL_TLS: ret = TLSRecv(ConnectionInfoSSL(conn_info), proto, CF_INBAND_OFFSET); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn_info)); ret = -1; } if (ret == -1 || ret == 0) return ret; LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction header: ", proto, ret); ret = sscanf(proto, "%c %u", &status, &len); if (ret != 2) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- bogus header: %s", proto); return -1; } if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- too long (len=%d)", len); return -1; } if (status != CF_MORE && status != CF_DONE) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- bogus header (more='%c')", status); return -1; } if (more != NULL) { switch (status) { case CF_MORE: *more = true; break; case CF_DONE: *more = false; break; default: ProgrammingError("Unreachable, " "bogus headers have already been checked!"); } } /* Get data. */ switch(ConnectionInfoProtocolVersion(conn_info)) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(ConnectionInfoSocket(conn_info), buffer, len); break; case CF_PROTOCOL_TLS: ret = TLSRecv(ConnectionInfoSSL(conn_info), buffer, len); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn_info)); ret = -1; } LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction data: ", buffer, ret); return ret; }
int CopyRegularFileNet(const char *source, const char *dest, off_t size, bool encrypt, AgentConnection *conn) { char *buf, workbuf[CF_BUFSIZE], cfchangedstr[265]; const int buf_size = 2048; off_t n_read_total = 0; EVP_CIPHER_CTX crypto_ctx; /* We encrypt only for CLASSIC protocol. The TLS protocol is always over * encrypted layer, so it does not support encrypted (S*) commands. */ encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC; if (encrypt) { return EncryptCopyRegularFileNet(source, dest, size, conn); } snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); if ((strlen(dest) > CF_BUFSIZE - 20)) { Log(LOG_LEVEL_ERR, "Filename too long"); return false; } unlink(dest); /* To avoid link attacks */ int dd = safe_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, 0600); if (dd == -1) { Log(LOG_LEVEL_ERR, "Copy from server '%s' to destination '%s' failed (open: %s)", conn->this_server, dest, GetErrorStr()); unlink(dest); return false; } workbuf[0] = '\0'; int tosend = snprintf(workbuf, CF_BUFSIZE, "GET %d %s", buf_size, source); if (tosend <= 0 || tosend >= CF_BUFSIZE) { Log(LOG_LEVEL_ERR, "Failed to compose GET command for file %s", source); close(dd); return false; } /* Send proposition C0 */ if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_ERR, "Couldn't send GET command"); close(dd); return false; } buf = xmalloc(CF_BUFSIZE + sizeof(int)); /* Note CF_BUFSIZE not buf_size !! */ Log(LOG_LEVEL_VERBOSE, "Copying remote file '%s:%s', expecting %jd bytes", conn->this_server, source, (intmax_t)size); n_read_total = 0; while (n_read_total < size) { int toget = MIN(size - n_read_total, buf_size); assert(toget != 0); /* Stage C1 - receive */ int n_read; switch(conn->conn_info->protocol) { case CF_PROTOCOL_CLASSIC: n_read = RecvSocketStream(conn->conn_info->sd, buf, toget); break; case CF_PROTOCOL_TLS: n_read = TLSRecv(conn->conn_info->ssl, buf, toget); break; default: UnexpectedError("CopyRegularFileNet: ProtocolVersion %d!", conn->conn_info->protocol); n_read = -1; } if (n_read <= 0) { /* This may happen on race conditions, where the file has shrunk * since we asked for its size in SYNCH ... STAT source */ Log(LOG_LEVEL_ERR, "Error in client-server stream, has %s:%s shrunk? (code %d)", conn->this_server, source, n_read); close(dd); free(buf); return false; } /* If the first thing we get is an error message, break. */ if ((n_read_total == 0) && (strncmp(buf, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (strncmp(buf, cfchangedstr, strlen(cfchangedstr)) == 0) { Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); free(buf); return false; } /* Check for mismatch between encryption here and on server. */ int value = -1; sscanf(buf, "t %d", &value); if ((value > 0) && (strncmp(buf + CF_INBAND_OFFSET, "BAD: ", 5) == 0)) { Log(LOG_LEVEL_INFO, "Network access to cleartext '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (!FSWrite(dest, dd, buf, n_read)) { Log(LOG_LEVEL_ERR, "Local disk write failed copying '%s:%s' to '%s'. (FSWrite: %s)", conn->this_server, source, dest, GetErrorStr()); if (conn) { conn->error = true; } free(buf); unlink(dest); close(dd); FlushFileStream(conn->conn_info->sd, size - n_read_total); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } n_read_total += n_read; } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. Write a null character and truncate it again. */ if (ftruncate(dd, n_read_total) < 0) { Log(LOG_LEVEL_ERR, "Copy failed (no space?) while copying '%s' from network '%s'", dest, GetErrorStr()); free(buf); unlink(dest); close(dd); FlushFileStream(conn->conn_info->sd, size - n_read_total); return false; } close(dd); free(buf); return true; }