From 1958fe0081d461bb91bf0aaee0c92783126590df Mon Sep 17 00:00:00 2001 From: Lars Erik Wik Date: Fri, 19 Jun 2026 14:31:12 +0200 Subject: [PATCH 1/2] Bumped libntech to master Signed-off-by: Lars Erik Wik --- libntech | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntech b/libntech index aa3cbf893d..c91951ff41 160000 --- a/libntech +++ b/libntech @@ -1 +1 @@ -Subproject commit aa3cbf893d121c67504b5ecfec6bc147ca112725 +Subproject commit c91951ff4185de36f27f42f1cfed200a79061342 From 0e8e37b791253c90afd5b25ec3b07cd08e95ee32 Mon Sep 17 00:00:00 2001 From: Lars Erik Wik Date: Fri, 5 Jun 2026 16:20:31 +0200 Subject: [PATCH 2/2] Added GETPATCH protocol command for serving leech2 patches The hub sends 'GETPATCH ' over the established TLS connection, and the server replies with a leech2 patch using a new patch stream API, which reuses the wire format from the file stream protocol. The patch is created by an enterprise hook; CFEngine Community refuses the request. This introduces protocol version 5 - "leech2". Ticket: ENT-14104 Changelog: Title Signed-off-by: Lars Erik Wik --- cf-net/cf-net.c | 2 +- cf-serverd/cf-serverd-enterprise-stubs.c | 9 ++ cf-serverd/cf-serverd-enterprise-stubs.h | 1 + cf-serverd/server_tls.c | 68 ++++++++++++-- cf-serverd/server_tls.h | 2 + cf-testd/cf-testd.c | 22 +++++ libcfnet/Makefile.am | 1 + libcfnet/patch_stream.c | 113 +++++++++++++++++++++++ libcfnet/patch_stream.h | 104 +++++++++++++++++++++ libcfnet/protocol_version.c | 4 + libcfnet/protocol_version.h | 10 +- libpromises/mod_common.c | 2 +- libpromises/mod_files.c | 2 +- 13 files changed, 326 insertions(+), 14 deletions(-) create mode 100644 libcfnet/patch_stream.c create mode 100644 libcfnet/patch_stream.h diff --git a/cf-net/cf-net.c b/cf-net/cf-net.c index 82f9bbd7b7..599f4d73d8 100644 --- a/cf-net/cf-net.c +++ b/cf-net/cf-net.c @@ -133,7 +133,7 @@ static const char *const HINTS[] = "Enable basic information output", "Minimum TLS version to use", "TLS ciphers to use (comma-separated list)", - "Specify CFEngine protocol to use. Possible values: 'classic', 'tls', 'cookie', 'filestream', 'latest' (default)", + "Specify CFEngine protocol to use. Possible values: 'classic', 'tls', 'cookie', 'filestream', 'leech2', 'latest' (default)", "Print rsync performance statistics to stderr", NULL }; diff --git a/cf-serverd/cf-serverd-enterprise-stubs.c b/cf-serverd/cf-serverd-enterprise-stubs.c index e45ca0b4a5..6471eebde2 100644 --- a/cf-serverd/cf-serverd-enterprise-stubs.c +++ b/cf-serverd/cf-serverd-enterprise-stubs.c @@ -26,6 +26,8 @@ #include #include +#include /* PatchStreamRefuse */ +#include /* ConnectionInfoSSL */ ENTERPRISE_VOID_FUNC_3ARG_DEFINE_STUB(void, RegisterLiteralServerData, ARG_UNUSED EvalContext *, ctx, @@ -63,6 +65,13 @@ ENTERPRISE_FUNC_1ARG_DEFINE_STUB(bool, ReturnCookies, ARG_UNUSED ServerConnectio return false; } +ENTERPRISE_FUNC_2ARG_DEFINE_STUB(bool, ServeLeech2Patch, ServerConnectionState *, conn, ARG_UNUSED const char *, last_hash) +{ + assert(conn != NULL); + Log(LOG_LEVEL_VERBOSE, "Serving leech2 patches is only available in CFEngine Enterprise"); + return PatchStreamRefuse(ConnectionInfoSSL(conn->conn_info)); +} + ENTERPRISE_FUNC_3ARG_DEFINE_STUB(bool, ReturnQueryData, ARG_UNUSED ServerConnectionState *, conn, ARG_UNUSED char *, menu, ARG_UNUSED int, encrypt) { return false; diff --git a/cf-serverd/cf-serverd-enterprise-stubs.h b/cf-serverd/cf-serverd-enterprise-stubs.h index c3159b3aa7..0388262023 100644 --- a/cf-serverd/cf-serverd-enterprise-stubs.h +++ b/cf-serverd/cf-serverd-enterprise-stubs.h @@ -41,6 +41,7 @@ typedef void (*ServerEntryPointFunction)(EvalContext *ctx, char *ipaddr, Connect ENTERPRISE_FUNC_1ARG_DECLARE(bool, ReceiveCollectCall, ServerConnectionState *, conn); ENTERPRISE_FUNC_1ARG_DECLARE(bool, ReturnCookies, ServerConnectionState *, conn); +ENTERPRISE_FUNC_2ARG_DECLARE(bool, ServeLeech2Patch, ServerConnectionState *, conn, const char *, last_hash); ENTERPRISE_FUNC_3ARG_DECLARE(bool, ReturnQueryData, ServerConnectionState *, conn, char *, menu, int, encrypt); ENTERPRISE_FUNC_2ARG_DECLARE(bool, CFTestD_ReturnQueryData, ServerConnectionState *, conn, char *, menu); diff --git a/cf-serverd/server_tls.c b/cf-serverd/server_tls.c index edc185237b..0f4e142db3 100644 --- a/cf-serverd/server_tls.c +++ b/cf-serverd/server_tls.c @@ -36,11 +36,13 @@ #include /* LastSaw1 */ #include /* SendTransaction,ReceiveTransaction */ #include /* TLSSend */ +#include /* PatchStreamRefuse */ #include #include #include /* StringMatchFull */ #include #include /* IsDirReal */ +#include #include "server_access.h" /* access_CheckResource, acl_CheckExact */ @@ -706,7 +708,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) char *args = &recvbuffer[EXEC_len]; args += strspn(args, " \t"); /* bypass spaces */ - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "EXEC", args); bool b = DoExec2(ctx, conn, args, @@ -736,7 +738,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) goto protocol_error; } - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "GET", filename); /* TODO batch all the following in one function since it's very @@ -760,7 +762,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) PathRemoveTrailingSlash(filename, strlen(filename)); - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Translated to:", "GET", filename); if (acl_CheckPath(paths_acl, filename, @@ -799,7 +801,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) goto protocol_error; } - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "OPENDIR", filename); /* sizeof()-1 because we need one extra byte for @@ -823,7 +825,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) /* OPENDIR *must* be directory. */ PathAppendTrailingSlash(filename, strlen(filename)); - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Translated to:", "OPENDIR", filename); if (acl_CheckPath(paths_acl, filename, @@ -863,7 +865,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) time_t trem = (time_t) time_no_see; int drift = (int) (tloc - trem); - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "STAT", filename); /* sizeof()-1 because we need one extra byte for @@ -893,7 +895,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) PathRemoveTrailingSlash(filename, strlen(filename)); } - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Translated to:", "STAT", filename); if (acl_CheckPath(paths_acl, filename, @@ -931,7 +933,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) goto protocol_error; } - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "MD5", filename); /* TODO batch all the following in one function since it's very @@ -955,7 +957,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) PathRemoveTrailingSlash(filename, strlen(filename)); - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Translated to:", "MD5", filename); if (acl_CheckPath(paths_acl, filename, @@ -1013,7 +1015,7 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) goto protocol_error; } - Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", + Log(LOG_LEVEL_VERBOSE, "%14s %8s %s", "Received:", "CONTEXT", client_regex); /* WARNING: this comes from legacy code and must be killed if we care @@ -1116,6 +1118,52 @@ bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) break; } + case PROTOCOL_COMMAND_GETPATCH: + { + if (ConnectionInfoProtocolVersion(conn->conn_info) < CF_PROTOCOL_LEECH2) + { + goto protocol_error; + } + + char last_hash[41]; + int ret = sscanf(recvbuffer, "GETPATCH %40s", last_hash); + if (ret != 1) + { + goto protocol_error; + } + + Log(LOG_LEVEL_VERBOSE, "%14s %8s %.7s...", + "Received:", "GETPATCH", last_hash); + + if (!StringIsSHA1Hex(last_hash)) + { + goto protocol_error; + } + + const char *hostkey = KeyPrintableHash( + ConnectionInfoKey(conn->conn_info)); + const bool access_to_query_delta = acl_CheckExact( + query_acl, "delta", conn->ipaddr, conn->revdns, hostkey); + + if (!access_to_query_delta) + { + Log(LOG_LEVEL_INFO, "access denied to GETPATCH: %s", recvbuffer); + /* Refuse using the stream protocol, as opposed to RefuseAccess(), + * because the client expects stream protocol messages after + * sending the GETPATCH request. + * + * Access denied is not fatal to the connection, so keep it open on + * success. However, if sending the refusal failed, it means that + * the connection is broken (already logged), so close it by + * returning false. */ + return PatchStreamRefuse(ConnectionInfoSSL(conn->conn_info)); + } + + /* Keep the connection open on success. If serving the patch stream + * failed, it means that the connection is broken (already logged), so + * close it by returning false */ + return ServeLeech2Patch(conn, last_hash); + } case PROTOCOL_COMMAND_CALL_ME_BACK: /* Server side, handing the collect call off to cf-hub. */ diff --git a/cf-serverd/server_tls.h b/cf-serverd/server_tls.h index e9c1114d97..196d1efe43 100644 --- a/cf-serverd/server_tls.h +++ b/cf-serverd/server_tls.h @@ -46,6 +46,7 @@ typedef enum PROTOCOL_COMMAND_QUERY, PROTOCOL_COMMAND_CALL_ME_BACK, PROTOCOL_COMMAND_COOKIE, + PROTOCOL_COMMAND_GETPATCH, PROTOCOL_COMMAND_BAD } ProtocolCommandNew; @@ -62,6 +63,7 @@ static const char *const PROTOCOL_NEW[PROTOCOL_COMMAND_BAD + 1] = "QUERY", "SCALLBACK", "COOKIE", + "GETPATCH", NULL }; diff --git a/cf-testd/cf-testd.c b/cf-testd/cf-testd.c index f089ce118a..f6f64c47ef 100644 --- a/cf-testd/cf-testd.c +++ b/cf-testd/cf-testd.c @@ -40,6 +40,7 @@ #include // ThreadLock #include #include // ERR_get_error +#include // PatchStreamServe #include // PolicyServerReadFile #include // PRINTSIZE #include // acl_Free @@ -345,6 +346,8 @@ static bool CFTestD_ProtocolError( static bool CFTestD_BusyLoop( ServerConnectionState *conn, CFTestD_Config *config) { + assert(conn != NULL); + char recvbuffer[CF_BUFSIZE + CF_BUFEXT] = ""; char sendbuffer[CF_BUFSIZE - CF_INBAND_OFFSET] = ""; @@ -405,6 +408,25 @@ static bool CFTestD_BusyLoop( break; } + case PROTOCOL_COMMAND_GETPATCH: + { + char last_hash[64]; + int ret = sscanf(recvbuffer, "GETPATCH %63s", last_hash); + if (ret != 1) + { + break; + } + + /* cf-testd has no leech2 state; serve an empty patch so that the + * hub's GETPATCH request succeeds. */ + Log(LOG_LEVEL_INFO, "Serving empty leech2 patch"); + if (PatchStreamServe(ConnectionInfoSSL(conn->conn_info), "", 0)) + { + return true; + } + + break; + } case PROTOCOL_COMMAND_BAD: default: Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer); diff --git a/libcfnet/Makefile.am b/libcfnet/Makefile.am index 38b5b566fa..1a18c3c14c 100644 --- a/libcfnet/Makefile.am +++ b/libcfnet/Makefile.am @@ -42,6 +42,7 @@ libcfnet_la_SOURCES = \ key.c key.h \ misc.c \ net.c net.h \ + patch_stream.c patch_stream.h \ policy_server.c policy_server.h \ protocol.c protocol.h \ protocol_version.c protocol_version.h \ diff --git a/libcfnet/patch_stream.c b/libcfnet/patch_stream.c new file mode 100644 index 0000000000..18b467704b --- /dev/null +++ b/libcfnet/patch_stream.c @@ -0,0 +1,113 @@ +/* + Copyright 2026 Northern.tech AS + + This file is part of CFEngine 3 - written and maintained by Northern.tech AS. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + To the extent this program is licensed as part of the Enterprise + versions of CFEngine, the applicable Commercial Open Source License + (COSL) may apply to this file if you as a licensee so wish it. See + included file COSL.txt. +*/ + +#include + +#include + +#include +#include +#include + +bool PatchStreamRefuse(SSL *conn) +{ + assert(conn != NULL); + + /* The client sends nothing after the request line, so there is no need + * to flush the stream before sending the error message. */ + return ProtocolSendError(conn, false, ERROR_MSG_UNSPECIFIED_SERVER_REFUSAL); +} + +bool PatchStreamServe(SSL *conn, const void *data, size_t len) +{ + assert(conn != NULL); + assert(data != NULL || len == 0); + + const char *buf = data; + size_t offset = 0; + bool eof = false; + + while (!eof) + { + size_t chunk = len - offset; + if (chunk > PROTOCOL_MESSAGE_SIZE) + { + chunk = PROTOCOL_MESSAGE_SIZE; + } + eof = (offset + chunk == len); + + if (!ProtocolSendMessage(conn, buf + offset, chunk, eof)) + { + /* Error is already logged in ProtocolSendMessage() */ + return false; + } + offset += chunk; + } + + return true; +} + +bool PatchStreamFetch(SSL *conn, char **data, size_t *len) +{ + assert(conn != NULL); + assert(data != NULL); + assert(len != NULL); + + char *buf = NULL; + size_t buf_len = 0; + + char msg[PROTOCOL_MESSAGE_SIZE]; + size_t msg_len; + bool eof = false; + + while (!eof) + { + if (!ProtocolRecvMessage(conn, msg, &msg_len, &eof)) + { + /* Error is already logged in ProtocolRecvMessage() */ + free(buf); + return false; + } + + if (msg_len > 0) + { + if (msg_len > PATCH_STREAM_MAX_SIZE - buf_len) + { + Log(LOG_LEVEL_ERR, + "Refusing to fetch patch: size exceeds maximum of %d bytes", + PATCH_STREAM_MAX_SIZE); + free(buf); + return false; + } + + buf = xrealloc(buf, buf_len + msg_len); + memcpy(buf + buf_len, msg, msg_len); + buf_len += msg_len; + } + } + + *data = buf; + *len = buf_len; + return true; +} diff --git a/libcfnet/patch_stream.h b/libcfnet/patch_stream.h new file mode 100644 index 0000000000..7cd8b2c636 --- /dev/null +++ b/libcfnet/patch_stream.h @@ -0,0 +1,104 @@ +/* + Copyright 2026 Northern.tech AS + + This file is part of CFEngine 3 - written and maintained by Northern.tech AS. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + To the extent this program is licensed as part of the Enterprise + versions of CFEngine, the applicable Commercial Open Source License + (COSL) may apply to this file if you as a licensee so wish it. See + included file COSL.txt. +*/ + +#ifndef PATCH_STREAM_H +#define PATCH_STREAM_H + +/** + * @file patch_stream.h + * + * +---------+ +---------+ + * | client | | server | + * +----+----+ +----+----+ + * | | + * | Send("GETPATCH ") ------->| patch = Create(hash) + * | | + * | patch = Recv() <----------------| Send(patch) + * | | + * v v + * + * 1. Client (the hub) requests a leech2 patch containing the changes since + * the last known block hash + * 2. Server creates a patch from its leech2 block chain + * 3. Server sends the patch to the client + * + * The patch is an opaque byte buffer to this API; creating it (server side) + * and applying it (client side) is up to the caller. + */ + +#include +#include + +/** + * @brief The leech2 genesis block hash (40 zeros) + * + * Requesting a patch since genesis yields a full state patch. + */ +#define PATCH_STREAM_GENESIS_HASH "0000000000000000000000000000000000000000" + +/** + * @brief Maximum size (in bytes) of a patch accepted by PatchStreamFetch() + * + * Bounds how much memory we'll buffer for a single patch, so that a malicious + * or buggy peer cannot stream chunks forever and exhaust our memory. + */ +#define PATCH_STREAM_MAX_SIZE (256 * 1024 * 1024) + +/** + * @brief Reply with unspecified server refusal + * + * E.g., use this function when access is denied or when the server is unable + * to serve patches (e.g., leech2 is not available). We don't distinguish + * between these reasons for security purposes. + * + * @param conn The SSL connection object + * @return true on success, otherwise false + */ +bool PatchStreamRefuse(SSL *conn); + +/** + * @brief Serve a patch using the stream API + * + * @param conn The SSL connection object + * @param data The patch buffer + * @param len The length of the patch buffer + * @return true on success, otherwise false + */ +bool PatchStreamServe(SSL *conn, const void *data, size_t len); + +/** + * @brief Fetch a patch using the stream API + * + * @param conn The SSL connection object + * @param data Is set to the received patch buffer (caller takes ownership + * and must free it with free()). Is set to NULL if and only if + * len is set to 0 (i.e. no payload bytes were received); + * free(NULL) is a no-op, so the caller can free() it regardless. + * @param len Is set to the length of the received patch buffer + * @return true on success, otherwise false (including when the patch would + * exceed PATCH_STREAM_MAX_SIZE) + */ +bool PatchStreamFetch(SSL *conn, char **data, size_t *len); + +#endif // PATCH_STREAM_H diff --git a/libcfnet/protocol_version.c b/libcfnet/protocol_version.c index e99ef2c89d..56049c582c 100644 --- a/libcfnet/protocol_version.c +++ b/libcfnet/protocol_version.c @@ -37,6 +37,10 @@ ProtocolVersion ParseProtocolVersionPolicy(const char *const s) { return CF_PROTOCOL_FILESTREAM; } + else if (StringEqual(s, "5") || StringEqual(s, "leech2")) + { + return CF_PROTOCOL_LEECH2; + } else if (StringEqual(s, "latest")) { return CF_PROTOCOL_LATEST; diff --git a/libcfnet/protocol_version.h b/libcfnet/protocol_version.h index 61e9cf3855..14d9030305 100644 --- a/libcfnet/protocol_version.h +++ b/libcfnet/protocol_version.h @@ -40,10 +40,11 @@ typedef enum CF_PROTOCOL_TLS = 2, CF_PROTOCOL_COOKIE = 3, CF_PROTOCOL_FILESTREAM = 4, + CF_PROTOCOL_LEECH2 = 5, } ProtocolVersion; /* We use CF_PROTOCOL_LATEST as the default for new connections. */ -#define CF_PROTOCOL_LATEST CF_PROTOCOL_FILESTREAM +#define CF_PROTOCOL_LATEST CF_PROTOCOL_LEECH2 static inline const char *ProtocolVersionString(const ProtocolVersion p) { @@ -57,6 +58,8 @@ static inline const char *ProtocolVersionString(const ProtocolVersion p) return "classic"; case CF_PROTOCOL_FILESTREAM: return "filestream"; + case CF_PROTOCOL_LEECH2: + return "leech2"; default: return "undefined"; } @@ -92,6 +95,11 @@ static inline bool ProtocolSupportsFileStream(const ProtocolVersion p) return (p >= CF_PROTOCOL_FILESTREAM); } +static inline bool ProtocolSupportsLeech2(const ProtocolVersion p) +{ + return (p >= CF_PROTOCOL_LEECH2); +} + static inline bool ProtocolTerminateCSV(const ProtocolVersion p) { return (p < CF_PROTOCOL_COOKIE); diff --git a/libpromises/mod_common.c b/libpromises/mod_common.c index 7b1a59967a..918f3fb868 100644 --- a/libpromises/mod_common.c +++ b/libpromises/mod_common.c @@ -264,7 +264,7 @@ const ConstraintSyntax CFG_CONTROLBODY[COMMON_CONTROL_MAX + 1] = ConstraintSyntaxNewBool("fips_mode", "Activate full FIPS mode restrictions. Default value: false", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewReal("bwlimit", CF_VALRANGE, "Limit outgoing protocol bandwidth in Bytes per second", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewBool("cache_system_functions", "Cache the result of system functions. Default value: true", SYNTAX_STATUS_NORMAL), - ConstraintSyntaxNewOption("protocol_version", "1,classic,2,tls,3,cookie,4,filestream,latest", "CFEngine protocol version to use when connecting to the server. Default: \"latest\"", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewOption("protocol_version", "1,classic,2,tls,3,cookie,4,filestream,5,leech2,latest", "CFEngine protocol version to use when connecting to the server. Default: \"latest\"", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewString("tls_ciphers", "", "List of acceptable ciphers in outgoing TLS connections, defaults to OpenSSL's default. For syntax help see man page for \"openssl ciphers\"", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewString("tls_min_version", "", "Minimum acceptable TLS version for outgoing connections, defaults to OpenSSL's default", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewStringList("package_inventory", ".*", "Name of the package manager used for software inventory management", SYNTAX_STATUS_NORMAL), diff --git a/libpromises/mod_files.c b/libpromises/mod_files.c index 74ddf19a23..4d501dff62 100644 --- a/libpromises/mod_files.c +++ b/libpromises/mod_files.c @@ -335,7 +335,7 @@ static const ConstraintSyntax copy_from_constraints[] = ConstraintSyntaxNewBool("trustkey", "true/false trust public keys from remote server if previously unknown. Default value: false", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewBool("type_check", "true/false compare file types before copying and require match", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewBool("verify", "true/false verify transferred file by hashing after copy (resource penalty). Default value: false", SYNTAX_STATUS_NORMAL), - ConstraintSyntaxNewOption("protocol_version", "1,classic,2,tls,3,cookie,4,filestream,latest", "CFEngine protocol version to use when connecting to the server. Default: undefined", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewOption("protocol_version", "1,classic,2,tls,3,cookie,4,filestream,5,leech2,latest", "CFEngine protocol version to use when connecting to the server. Default: undefined", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewBool("missing_ok", "true/false Do not treat missing file as an error. Default value: false", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewNull() };