From patchwork Thu Jan 16 12:11:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "mrbff (Code Review)" X-Patchwork-Id: 4064 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:b7cb:b0:5e7:b9eb:58e8 with SMTP id en11csp80582mab; Thu, 16 Jan 2025 04:12:03 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCV1uYfkSaiwxB08T5gRSyHc500wdV6lzfxRLZs9PUdydeQ6Lr0CewQsxidK3q5a3bHEDGQfEMC7bag=@openvpn.net X-Google-Smtp-Source: AGHT+IEoXw4SGuVzSnBBiLOTHWpEGkZ/Jl7UGCrUvWD2udmoAy3JwxWMid+LmAETGEmzZeOak8PG X-Received: by 2002:a05:6871:6302:b0:29e:563c:2dbf with SMTP id 586e51a60fabf-2aa0690ed1fmr20147874fac.26.1737029522684; Thu, 16 Jan 2025 04:12:02 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1737029522; cv=none; d=google.com; s=arc-20240605; b=adN/fzPtiiOdizrhmDC5dnQffa9WvzKA+vbOrKARm/S21c6kL7twJaNBGJiPYMa9zm vAVk8tvNfsm5tPuVTrwx05D/10q0D68zeACCMEzd3URUJK2a6YhG1pRTDD1i3AUv729a 6kHM+PxWyEMP7170Tx/vTjcFSm4uYuRpd/iaA0ogQ9v0qXcIaoulH+McxC6vWmbT/AGx Gmp5uU3Z2EMK9S9qk47c9psBnJT+gUECIwW5AWAyT7WjNpyOVi2GdeU5uxpb6lnjVDqL SmGSj/Weauq9huh1C2wphJvWH/LeiMb7Pos11k8G6b8pKn//zv1S3EbwLprvLHb0Xbl6 O6gQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:cc:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:user-agent :mime-version:message-id:references:auto-submitted:to:date:from :dkim-signature:dkim-signature:dkim-signature; bh=s3w9aA0OuswyWsWiWSrlIbLLn8QH0TSVp2ezECuytOg=; fh=U7wEyxtwz2o5+UdevFSA47vNeG9knhWH0KV//QhD5a0=; b=UCNydxh2KsXNKjf67TiuGXkJxIu3U1v1CafPqt062yjPgmN7Kbp56++z9fNa8rHczA ZeTY6t/rGZBulsPXmjOiui6L8/ZEL9sYiP/wqGLYJ2YICKKRT24dNxBTTTxxbHKsXE+c UpO7JwhZCjWEzdW6XJZ2r2ywLLPcrwAsGzP7z04xjLkvquupB/ZtBli9x5O9WyTtJAIV 0Qh1vKFlmq43GVVUc2BbrYRokbJNO8KuqvTqY2ASqBc7g2NO+gEMKdQkkP/Nz16D+tnC 2JncnrV7dO8mknapQr+CR3N1kkYKwr6HfaX5BZ3b6zzDFdJAB2AX2M/kx8yWj832sKaU iVuw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=YDD2+TJV; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=ZOXcU0xv; dkim=neutral (body hash did not verify) header.i=@openvpn.net header.s=google header.b=LDiVtCtc; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=openvpn.net; dara=fail header.i=@openvpn.net Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 586e51a60fabf-2ad809ad794si11852126fac.132.2025.01.16.04.12.02 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 16 Jan 2025 04:12:02 -0800 (PST) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=YDD2+TJV; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=ZOXcU0xv; dkim=neutral (body hash did not verify) header.i=@openvpn.net header.s=google header.b=LDiVtCtc; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=openvpn.net; dara=fail header.i=@openvpn.net Received: from [127.0.0.1] (helo=sfs-ml-3.v29.lw.sourceforge.com) by sfs-ml-3.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1tYOj7-0004BX-1Y; Thu, 16 Jan 2025 12:11:56 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-3.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1tYOj5-0004BR-CS for openvpn-devel@lists.sourceforge.net; Thu, 16 Jan 2025 12:11:54 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Type:Content-Transfer-Encoding:MIME-Version :Message-ID:Reply-To:References:Subject:List-Unsubscribe:List-Id:Cc:To:Date: From:Sender:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:List-Help: List-Subscribe:List-Post:List-Owner:List-Archive; bh=oL9JJlhgBq0KkfcDTv8MPT4seuaXWlze1gY1C67zbBE=; b=YDD2+TJVh0weqcqRXVaOXDofW6 VQtWLJSXuYU91RH+wZqmYeHMda1PCunF+Szis+B2RU4E+rjg4LECkhQsaYvOzJhTlcP16l5F5Df6X Oamngbt/dUNd9NbrKuLf7JlHweuhWU04EvyQtkSWOoKnETatFCiZrwoVSvUzRCtJQuls=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Type:Content-Transfer-Encoding:MIME-Version:Message-ID:Reply-To: References:Subject:List-Unsubscribe:List-Id:Cc:To:Date:From:Sender:Content-ID :Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To: Resent-Cc:Resent-Message-ID:In-Reply-To:List-Help:List-Subscribe:List-Post: List-Owner:List-Archive; bh=oL9JJlhgBq0KkfcDTv8MPT4seuaXWlze1gY1C67zbBE=; b=Z OXcU0xvlTHLyltd1wabLxgNsAmZ1Fyh0XDI6uw6y6dOMZ8FFN6U+P+sFtgGBtr65Kf5y4SafqYYJA pMvrl6YXE74G9+7yJ5j2wd/oGP4FU+5/raQxGFgSuDyDwAHDajPNb7SAdLK1J32NZSeBewwjh3pxy gEWavGjSVnD3UJiI=; Received: from mail-wm1-f52.google.com ([209.85.128.52]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.95) id 1tYOj0-0006xa-QH for openvpn-devel@lists.sourceforge.net; Thu, 16 Jan 2025 12:11:54 +0000 Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-4362bae4d7dso5119185e9.1 for ; Thu, 16 Jan 2025 04:11:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvpn.net; s=google; t=1737029499; x=1737634299; darn=lists.sourceforge.net; h=user-agent:content-disposition:content-transfer-encoding :mime-version:message-id:reply-to:references:subject :list-unsubscribe:list-id:auto-submitted:cc:to:date:from:from:to:cc :subject:date:message-id:reply-to; bh=oL9JJlhgBq0KkfcDTv8MPT4seuaXWlze1gY1C67zbBE=; b=LDiVtCtc0xyP4keOJ20Xl9RmK9X7bqlIistTup+osfKCHzED5bc+F8Xr3iTCSsRWLV szTXHaF6lVKSkBJslyv7IsYiCGfISvZaVIhtA7KGOiwrQDp+gS2SLs+WQWP6LBRFHJtx 86w3Za824Yo1EzPQd9Z50jYjnWguXw+Qrgp6Tz5ubRxChq4AYy9edtUT5lKaWxu8ABTx xl+2U74QClqfhY7qMhOzt2qvLVsx2QXFxt/irEzpm7bASzGjdDybJOwINyvmhHVg+9I1 NDGvOhL7wU8jEX5u/XgEbW3LrACHRG3zRJ5e3l4v2d4G7fBU80avtcbHoJafEQA8A5x1 sCJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737029499; x=1737634299; h=user-agent:content-disposition:content-transfer-encoding :mime-version:message-id:reply-to:references:subject :list-unsubscribe:list-id:auto-submitted:cc:to:date:from :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=oL9JJlhgBq0KkfcDTv8MPT4seuaXWlze1gY1C67zbBE=; b=BMjpoBhcK1yqqPFLD26s2VlQDCkZ/ixXUKszxtyoXm44fHX2g4Gd2FlFk8QlQQ9CRc JlyMrYV0Ddc4qX/HJIlkOGtgXZOUIBbbXYCRim0KRaA2NPzCvqX2vpxpR68Oa99cYeU8 jzClJoOV3QfWQpiXmQ2CcxvtLuWjlqJ49Rn+AN2kzzrt6YqYXtGSVxMaaXbqNx0M1UOH Gv9J1zqAE9yQ3HJOsVgToE2D61EN3cgcVYeYRFi8IKDLFl5UjTv1Jg4xIL7gsioU2Sf/ +ADnfPtKcryQyLFHSfnxi+g85hhiIMuvq2N+KpXuPtf2H/8YQfeVbfmLvJEDJ4hyqHDM a9JA== X-Gm-Message-State: AOJu0YzuwoWVI1uHHm2MZILbMofImcQ1JMJrS9R0C83Ppyks4xMiDF1o 3eGW3dWA+FsWefpOS38GRIKaCwNNRR3yTEUj6RYM92EP5+kiZsU3KRmhAXQ+o9k= X-Gm-Gg: ASbGncs+XHGS3DiEVq5uWSuE7djACYqmg5q3+cfjn3leriBkp2ptsEuMIke0UtQUGIQ mmw9uQPmIlttfRGggEg5XVum1Xj3g21mwZ9/f40BsIclkpOlG2RBEoX7vtrukQ7YBOIhTu5MUDX YLnPAc+BSyhaqSryvAc4PqEkeAOiAQnbRTol4nz99KSk0NC0ftroaIyJPRbCc3dX3hmmrWW9RLs xj5XNmQsgLtbEEPjV3zxjDPyGn0EQs5UpkGNCSySHcneKJBJ7EU9Ibcui3HbQ0UXg49u4uf6Gro W05JxaGCXKpUF0ozIr894o9FXWiGY7iS7fSN6dAjpfsCnvUf X-Received: by 2002:a05:600c:1e1b:b0:434:fa55:eb56 with SMTP id 5b1f17b1804b1-436e2686279mr314546265e9.7.1737029498621; Thu, 16 Jan 2025 04:11:38 -0800 (PST) Received: from gerrit.openvpn.in (ec2-18-159-0-78.eu-central-1.compute.amazonaws.com. [18.159.0.78]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-38a8e3834fbsm20065813f8f.26.2025.01.16.04.11.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jan 2025 04:11:38 -0800 (PST) From: "mrbff (Code Review)" X-Google-Original-From: "mrbff (Code Review)" X-Gerrit-PatchSet: 1 Date: Thu, 16 Jan 2025 12:11:37 +0000 To: plaisthos , flichtenheld Auto-Submitted: auto-generated X-Gerrit-MessageType: newchange X-Gerrit-Change-Id: Ie82bcc7a8e583de9156b185d71d1a323ed8df3fc X-Gerrit-Change-Number: 869 X-Gerrit-Project: openvpn X-Gerrit-ChangeURL: X-Gerrit-Commit: 985e01ffa402f98b32a7725b3207bbc69e25ea1d References: Message-ID: MIME-Version: 1.0 User-Agent: Gerrit/3.8.2 X-Spam-Score: -2.0 (--) X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit Content analysis details: (-2.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [209.85.128.52 listed in list.dnswl.org] 0.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [209.85.128.52 listed in bl.score.senderscore.com] 0.0 RCVD_IN_VALIDITY_SAFE_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [209.85.128.52 listed in sa-accredit.habeas.com] -1.8 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.128.52 listed in wl.mailspike.net] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.0 WEIRD_PORT URI: Uses non-standard port number for HTTP 0.0 HTML_MESSAGE BODY: HTML included in message -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.0 T_KAM_HTML_FONT_INVALID Test for Invalidly Named or Formatted Colors in HTML X-Headers-End: 1tYOj0-0006xa-QH Subject: [Openvpn-devel] [L] Change in openvpn[master]: PUSH_UPDATE message sender: enabling the server to send PUSH_UPDATE c... X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: marco@mandelbit.com, arne-openvpn@rfc2549.org, openvpn-devel@lists.sourceforge.net, frank@lichtenheld.com Cc: openvpn-devel Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1821407468702893634?= X-GMAIL-MSGID: =?utf-8?q?1821407468702893634?= X-getmail-filter-classifier: gerrit message type newchange Attention is currently required from: flichtenheld, plaisthos. Hello plaisthos, flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/869?usp=email to review the following change. Change subject: PUSH_UPDATE message sender: enabling the server to send PUSH_UPDATE control messages ...................................................................... PUSH_UPDATE message sender: enabling the server to send PUSH_UPDATE control messages Using the management interface you can now target one or more clients (via broadcast, via cid, via common name, via address) and send a PUSH_UPDATE control message to update some options. Change-Id: Ie82bcc7a8e583de9156b185d71d1a323ed8df3fc Signed-off-by: Marco Baffo --- M CMakeLists.txt M src/openvpn/manage.c M src/openvpn/manage.h M src/openvpn/multi.c M src/openvpn/multi.h M src/openvpn/push.h M src/openvpn/push_util.c M tests/unit_tests/openvpn/Makefile.am M tests/unit_tests/openvpn/test_push_update_msg.c 9 files changed, 729 insertions(+), 6 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/69/869/1 diff --git a/CMakeLists.txt b/CMakeLists.txt index dec3db4..a44134a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -818,6 +818,7 @@ src/openvpn/push_util.c src/openvpn/options_util.c src/openvpn/otime.c + src/openvpn/list.c ) if (TARGET test_argv) diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 0c77f85..0bbf93c 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -24,7 +24,6 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif - #include "syshead.h" #ifdef ENABLE_MANAGEMENT @@ -42,6 +41,7 @@ #include "manage.h" #include "openvpn.h" #include "dco.h" +#include "push.h" #include "memdbg.h" @@ -124,6 +124,11 @@ msg(M_CLIENT, "username type u : Enter username u for a queried OpenVPN username."); msg(M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent."); msg(M_CLIENT, "version [n] : Set client's version to n or show current version of daemon."); + msg(M_CLIENT, "push-update-broad options : Broadcast a message to update the specified options."); + msg(M_CLIENT, " Ex. push-update-broad \"route something, -dns\""); + msg(M_CLIENT, "push-update-cid CID options : Send an update message to the client identified by CID."); + msg(M_CLIENT, "push-update-cn CN options : Send an update message to the client(s) with the specified Common Name."); + msg(M_CLIENT, "push-update-addr ip port options : Send an update message to the client(s) connecting from the provided address."); msg(M_CLIENT, "END"); } @@ -1327,6 +1332,154 @@ } static void +man_push_update(struct management *man, const char **p, const push_update_type type) +{ + if (type == UPT_BROADCAST) + { + if (!man->persist.callback.push_update_broadcast) + { + man_command_unsupported("push-update-broad"); + return; + } + + const bool status = (*man->persist.callback.push_update_broadcast)(man->persist.callback.arg, p[1]); + + if (status) + { + msg(M_CLIENT, "SUCCESS: push-update-broad command succeeded"); + } + else + { + msg(M_CLIENT, "ERROR: push-update-broad command failed"); + } + } + else if (type == UPT_BY_CID) + { + if (!man->persist.callback.push_update_by_cid) + { + man_command_unsupported("push-update-cid"); + return; + } + + unsigned long cid = 0; + + if (!parse_cid(p[1], &cid)) + { + msg(M_CLIENT, "ERROR: push-update-cid fail during cid parsing"); + return; + } + + const bool status = (*man->persist.callback.push_update_by_cid)(man->persist.callback.arg, cid, p[2]); + + if (status) + { + msg(M_CLIENT, "SUCCESS: push-update-cid command succeeded"); + } + else + { + msg(M_CLIENT, "ERROR: push-update-cid command failed"); + } + } + else if (type == UPT_BY_CN) + { + if (!man->persist.callback.push_update_by_cn) + { + man_command_unsupported("push-update-cn"); + return; + } + + const bool status = (*man->persist.callback.push_update_by_cn)(man->persist.callback.arg, p[1], p[2]); + + if (status) + { + msg(M_CLIENT, "SUCCESS: push-update-cn command succeeded"); + } + else + { + msg(M_CLIENT, "ERROR: push-update-cn command failed"); + } + } + else if (type == UPT_BY_ADDR) + { + if (!man->persist.callback.push_update_by_addr) + { + man_command_unsupported("push-update-addr"); + return; + } + + const char *ip_str = p[1]; + const char *port_str = p[2]; + const char *options = p[3]; + + if (!strlen(ip_str) || !strlen(port_str)) + { + msg(M_CLIENT, "ERROR: push-update-addr parse"); + return; + } + + struct addrinfo *res = NULL; + int port = atoi(port_str); + + if (port < 1 || port > 65535) + { + msg(M_CLIENT, "ERROR: port number is out of range: %s", port_str); + return; + } + + int status = openvpn_getaddrinfo(GETADDR_MSG_VIRT_OUT, ip_str, port_str, 0, NULL, AF_UNSPEC, &res); + + if (status != 0 || !res) + { + msg(M_CLIENT, "ERROR: error resolving address: %s (%s)", ip_str, gai_strerror(status)); + return; + } + + struct addrinfo *rp; + bool found_client = false; + + /* Iterate through resolved addresses */ + for (rp = res; rp != NULL; rp = rp->ai_next) + { + struct openvpn_sockaddr saddr; + struct mroute_addr maddr; + + CLEAR(saddr); + switch (rp->ai_family) + { + case AF_INET: + saddr.addr.in4 = *((struct sockaddr_in *)rp->ai_addr); + break; + + case AF_INET6: + saddr.addr.in6 = *((struct sockaddr_in6 *)rp->ai_addr); + break; + + default: + continue; + } + + if (!mroute_extract_openvpn_sockaddr(&maddr, &saddr, true)) + { + continue; + } + + if ((*man->persist.callback.push_update_by_addr)(man->persist.callback.arg, &maddr, options)) + { + msg(M_CLIENT, "SUCCESS: push-update sent to %s:%d", ip_str, port); + found_client = true; + break; + } + } + + if (!found_client) + { + msg(M_CLIENT, "ERROR: no client found at address %s:%d", ip_str, port); + } + freeaddrinfo(res); + } +} + +static void man_dispatch_command(struct management *man, struct status_output *so, const char **p, const int nparms) { struct gc_arena gc = gc_new(); @@ -1648,6 +1801,34 @@ man_remote(man, p); } } + else if (streq(p[0], "push-update-broad")) + { + if (man_need(man, p, 1, 0)) + { + man_push_update(man, p, UPT_BROADCAST); + } + } + else if (streq(p[0], "push-update-cid")) + { + if (man_need(man, p, 2, 0)) + { + man_push_update(man, p, UPT_BY_CID); + } + } + else if (streq(p[0], "push-update-cn")) + { + if (man_need(man, p, 2, 0)) + { + man_push_update(man, p, UPT_BY_CN); + } + } + else if (streq(p[0], "push-update-addr")) + { + if (man_need(man, p, 3, 0)) + { + man_push_update(man, p, UPT_BY_ADDR); + } + } #if 1 else if (streq(p[0], "test")) { diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index f501543..8f463c6 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -44,7 +44,6 @@ #define MF_EXTERNAL_KEY_PSSPAD (1<<16) #define MF_EXTERNAL_KEY_DIGEST (1<<17) - #ifdef ENABLE_MANAGEMENT #include "misc.h" @@ -205,6 +204,10 @@ #endif unsigned int (*remote_entry_count)(void *arg); bool (*remote_entry_get)(void *arg, unsigned int index, char **remote); + bool (*push_update_broadcast)(void *arg, const char *options); + bool (*push_update_by_cid)(void *arg, unsigned long cid, const char *options); + bool (*push_update_by_cn)(void *arg, const char *cn, const char *options); + bool (*push_update_by_addr)(void *arg, const struct mroute_addr *maddr, const char *options); }; /* diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 5b693f9..6a9ce83 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -3981,7 +3981,7 @@ } } -static struct multi_instance * +struct multi_instance * lookup_by_cid(struct multi_context *m, const unsigned long cid) { if (m) @@ -4129,6 +4129,10 @@ cb.client_auth = management_client_auth; cb.client_pending_auth = management_client_pending_auth; cb.get_peer_info = management_get_peer_info; + cb.push_update_broadcast = management_callback_send_push_update_broadcast; + cb.push_update_by_cid = management_callback_send_push_update_by_cid; + cb.push_update_by_cn = management_callback_send_push_update_by_cn; + cb.push_update_by_addr = management_callback_send_push_update_by_addr; management_set_callback(management, &cb); } #endif /* ifdef ENABLE_MANAGEMENT */ diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 9b6834a..4bb6e21 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -714,5 +714,10 @@ */ void multi_assign_peer_id(struct multi_context *m, struct multi_instance *mi); +#ifdef ENABLE_MANAGEMENT +struct multi_instance * +lookup_by_cid(struct multi_context *m, const unsigned long cid); + +#endif #endif /* MULTI_H */ diff --git a/src/openvpn/push.h b/src/openvpn/push.h index b930c6c..112a2a5 100644 --- a/src/openvpn/push.h +++ b/src/openvpn/push.h @@ -42,6 +42,16 @@ #define PUSH_OPT_TO_REMOVE (1<<0) #define PUSH_OPT_OPTIONAL (1<<1) +/* Push-update message sender modes */ +typedef enum { + UPT_BROADCAST = 0, + UPT_BY_ADDR = 1, + UPT_BY_CN = 2, +#ifdef ENABLE_MANAGEMENT + UPT_BY_CID = 3 +#endif +} push_update_type; + int process_incoming_push_request(struct context *c); /** @@ -135,4 +145,33 @@ void receive_auth_pending(struct context *c, const struct buffer *buffer); +/** + * @brief A function to send a PUSH_UPDATE control message from server to client(s). + * + * @param m the multi_context, contains all the clients connected to this server. + * @param target the target to which to send the message. It should be: + * `NULL` if `type == UPT_BROADCAST`, + * a `mroute_addr *` if `type == UPT_BY_ADDR`, + * a `char *` if `type == UPT_BY_CN`, + * an `unsigned long *` if `type == UPT_BY_CID`. + * @param msg a string containing the options to send. + * @param type the way to address the message (broadcast, by cid, by cn, by address). + * @param push_bundle_size the maximum size of a bundle of pushed option. Just use PUSH_BUNDLE_SIZE macro. + * @return the number of clients to which the message was sent. + */ +int +send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size); + +#ifdef ENABLE_MANAGEMENT + +bool management_callback_send_push_update_broadcast(void *arg, const char *options); + +bool management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options); + +bool management_callback_send_push_update_by_cn(void *arg, const char *cn, const char *options); + +bool management_callback_send_push_update_by_addr(void *arg, const struct mroute_addr *maddr, const char *options); + +#endif /* ifdef ENABLE_MANAGEMENT*/ + #endif /* ifndef PUSH_H */ diff --git a/src/openvpn/push_util.c b/src/openvpn/push_util.c index b4d1e8b..69d7a9c 100644 --- a/src/openvpn/push_util.c +++ b/src/openvpn/push_util.c @@ -3,6 +3,8 @@ #endif #include "push.h" +#include "multi.h" +#include "ssl_verify.h" int process_incoming_push_update(struct context *c, @@ -42,3 +44,247 @@ return ret; } + +/** + * Return index of last `,` or `0` if it didn't find any. + * If there is a comma at index `0` it's an error anyway + */ +static int +find_first_comma_of_next_bundle(const char *str, int ix) +{ + while (ix > 0) + { + if (str[ix] == ',') + { + return ix; + } + ix--; + } + return 0; +} + +static char * +gc_strdup(const char *src, struct gc_arena *gc) +{ + char *ret = gc_malloc((strlen(src) + 1) * sizeof(char), true, gc); + + strcpy(ret, src); + return ret; +} + +/* It split the messagge (if necessay) and fill msgs with the message chunks. + * Return `false` on failure an `true` on success. + */ +static bool +message_splitter(char *str, char **mesgs, struct gc_arena *gc, const int safe_cap) +{ + if (!str || !*str) + { + return false; + } + + int i = 0; + int im = 0; + + while (*str) + { + /* sizeof(push_update_cmd) + ',' - '/0' */ + if (strlen(str) + sizeof(push_update_cmd) > safe_cap) + { + int ci = find_first_comma_of_next_bundle(str, safe_cap - sizeof(push_update_cmd)); + if (!ci) + { + /* if no commas were found go to fail, do not send any message */ + return false; + } + str[ci] = '\0'; + /* copy from i to (ci -1) */ + mesgs[im] = gc_strdup(str, gc); + i = ci + 1; + } + else + { + mesgs[im] = gc_strdup(str, gc); + i = strlen(str); + } + str = &str[i]; + im++; + } + return true; +} + +/* It actually send the already divided messagge to one single client */ +static bool +send_single_push_update(struct context *c, char **msgs, struct buffer *buf, struct gc_arena *gc, const int push_bundle_size) +{ + if (!msgs[0] || !*msgs[0]) + { + return false; + } + else if (!msgs[1] || !*msgs[1]) + { + buf_printf(buf, "%s%c%s", push_update_cmd, ',', msgs[0]); + if (!send_control_channel_string(c, BSTR(buf), D_PUSH)) + { + return false; + } + } + else + { + int i = 0; + while (msgs[i] && *msgs[i]) + { + if (msgs[i+1]) + { + buf_printf(buf, "%s%c%s%s", push_update_cmd, ',', msgs[i], ",push-continuation 2"); + } + else + { + buf_printf(buf, "%s%c%s%s", push_update_cmd, ',', msgs[i], ",push-continuation 1"); + } + + if (!send_control_channel_string(c, BSTR(buf), D_PUSH)) + { + return false; + } + *buf = alloc_buf_gc(push_bundle_size, gc); + i++; + } + } + return true; +} + +int +send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size) +{ + if (!msg || !*msg || !m + || (!target && type != UPT_BROADCAST)) + { + return -EINVAL; + } + + struct gc_arena gc = gc_new(); + struct buffer buf = alloc_buf_gc(push_bundle_size, &gc); + /* extra space for possible trailing ifconfig and push-continuation */ + const int extra = 84; + /* push_bundle_size is the maximum size of a message, so if the message + * we want to send exceeds that size we have to split it into smaller messages */ + const int safe_cap = BCAP(&buf) - extra; + int mexnum = (strlen(msg) / (safe_cap - sizeof(push_update_cmd))) + 1; + char **msgs = gc_malloc(sizeof(char *) * (mexnum + 1), true, &gc); + + msgs[mexnum] = NULL; + if (!message_splitter(gc_strdup(msg, &gc), msgs, &gc, safe_cap)) + { + gc_free(&gc); + return -EINVAL; + } + +#ifdef ENABLE_MANAGEMENT + if (type == UPT_BY_CID) + { + struct multi_instance *mi = lookup_by_cid(m, *((unsigned long *)target)); + + if (!mi) + { + return -ENOENT; + } + if (!mi->halt + && send_single_push_update(&mi->context, msgs, &buf, &gc, push_bundle_size)) + { + gc_free(&gc); + return 1; + } + } +#endif + + int count = 0; + struct hash_iterator hi; + const struct hash_element *he; + + hash_iterator_init(m->iter, &hi); + while ((he = hash_iterator_next(&hi))) + { + struct multi_instance *curr_mi = he->value; + + if (curr_mi->halt) + { + continue; + } + if (type == UPT_BY_ADDR && !mroute_addr_equal(target, &curr_mi->real)) + { + continue; + } + else if (type == UPT_BY_CN) + { + const char *curr_cn = tls_common_name(curr_mi->context.c2.tls_multi, false); + if (strcmp(curr_cn, target)) + { + continue; + } + } + /* Either we found a matching client or type is UPT_BROADCAST so we update every client */ + if (!send_single_push_update(&curr_mi->context, msgs, &buf, &gc, push_bundle_size)) + { + msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", + curr_mi->context.c2.tls_multi ? curr_mi->context.c2.tls_multi->peer_id : UINT32_MAX); + continue; + } + count++; + } + + hash_iterator_free(&hi); + gc_free(&gc); + return count; +} + +#ifdef ENABLE_MANAGEMENT +#define RETURN_UPDATE_STATUS(n_sent) \ + do { \ + if ((n_sent) > 0) { \ + msg(M_CLIENT, "SUCCESS: %d client(s) updated", (n_sent)); \ + return true; \ + } else { \ + msg(M_CLIENT, "ERROR: no client updated"); \ + return false; \ + } \ + } while (0) + + +bool +management_callback_send_push_update_broadcast(void *arg, const char *options) +{ + int n_sent = send_push_update(arg, NULL, options, UPT_BROADCAST, PUSH_BUNDLE_SIZE); + + RETURN_UPDATE_STATUS(n_sent); +} + +bool +management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options) +{ + int ret = send_push_update(arg, &cid, options, UPT_BY_CID, PUSH_BUNDLE_SIZE); + + if (ret == -ENOENT) + { + msg(M_CLIENT, "ERROR: no client found with CID: %lu", cid); + } + + return (ret > 0); +} + +bool +management_callback_send_push_update_by_cn(void *arg, const char *cn, const char *options) +{ + int n_sent = send_push_update(arg, cn, options, UPT_BY_CN, PUSH_BUNDLE_SIZE); + + RETURN_UPDATE_STATUS(n_sent); +} + +bool +management_callback_send_push_update_by_addr(void *arg, const struct mroute_addr *maddr, const char *options) +{ + int n_sent = send_push_update(arg, maddr, options, UPT_BY_ADDR, PUSH_BUNDLE_SIZE); + + RETURN_UPDATE_STATUS(n_sent); +} +#endif /* ifdef ENABLE_MANAGEMENT */ diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index 651c5f6..529b77b 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -325,4 +325,5 @@ $(top_srcdir)/src/openvpn/platform.c \ $(top_srcdir)/src/openvpn/push_util.c \ $(top_srcdir)/src/openvpn/options_util.c \ - $(top_srcdir)/src/openvpn/otime.c \ No newline at end of file + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/list.c \ No newline at end of file diff --git a/tests/unit_tests/openvpn/test_push_update_msg.c b/tests/unit_tests/openvpn/test_push_update_msg.c index d0876bc..1a9e093 100644 --- a/tests/unit_tests/openvpn/test_push_update_msg.c +++ b/tests/unit_tests/openvpn/test_push_update_msg.c @@ -8,6 +8,7 @@ #include #include "push.h" #include "options_util.h" +#include "multi.h" /* mocks */ @@ -94,6 +95,48 @@ } } +const char * +tls_common_name(const struct tls_multi *multi, const bool null) +{ + return NULL; +} + +#ifndef ENABLE_MANAGEMENT +bool +send_control_channel_string(struct context *c, const char *str, int msglevel) +{ + return true; +} +#else /* ifndef ENABLE_MANAGEMENT */ +char **res; +int i; + +bool +send_control_channel_string(struct context *c, const char *str, int msglevel) +{ + if (res && res[i] && strcmp(res[i], str)) + { + return false; + } + i++; + return true; +} + +struct multi_instance * +lookup_by_cid(struct multi_context *m, const unsigned long cid) +{ + return *(m->instances); +} + +bool +mroute_extract_openvpn_sockaddr(struct mroute_addr *addr, + const struct openvpn_sockaddr *osaddr, + bool use_port) +{ + return true; +} +#endif /* ifndef ENABLE_MANAGEMENT */ + /* tests */ static void @@ -124,7 +167,6 @@ free_buf(&buf); } - static void test_incoming_push_message_error2(void **state) { @@ -209,6 +251,194 @@ free_buf(&buf); } +#ifdef ENABLE_MANAGEMENT +char *r0[] = { + "PUSH_UPDATE,redirect-gateway local,route 192.168.1.0 255.255.255.0" +}; +char *r1[] = { + "PUSH_UPDATE,-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf,push-continuation 2", + "PUSH_UPDATE, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8,redirect-gateway local,push-continuation 2", + "PUSH_UPDATE,route 192.168.1.0 255.255.255.0,push-continuation 1" +}; +char *r3[] = { + "PUSH_UPDATE,,," +}; +char *r4[] = { + "PUSH_UPDATE,-dhcp-option, blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf,push-continuation 2", + "PUSH_UPDATE, akakakakakakakakakakakaf,dhcp-option DNS 8.8.8.8, redirect-gateway local,push-continuation 2", + "PUSH_UPDATE, route 192.168.1.0 255.255.255.0,,push-continuation 1" +}; +char *r5[] = { + "PUSH_UPDATE,,-dhcp-option, blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf,push-continuation 2", + "PUSH_UPDATE, akakakakakakakakakakakaf,dhcp-option DNS 8.8.8.8, redirect-gateway local,push-continuation 2", + "PUSH_UPDATE, route 192.168.1.0 255.255.255.0,push-continuation 1" +}; +char *r6[] = { + "PUSH_UPDATE,-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf,push-continuation 2", + "PUSH_UPDATE, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8, redirect-gateway 10.10.10.10,,push-continuation 2", + "PUSH_UPDATE, route 192.168.1.0 255.255.255.0,,push-continuation 1" +}; +char *r7[] = { + "PUSH_UPDATE,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,push-continuation 2", + "PUSH_UPDATE,,,,,,,,,,,,,,,,,,,push-continuation 1" +}; +char *r8[] = { + "PUSH_UPDATE,-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf,push-continuation 2", + "PUSH_UPDATE, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8,redirect-gateway\n local,push-continuation 2", + "PUSH_UPDATE,route 192.168.1.0 255.255.255.0\n\n\n,push-continuation 1" +}; +char *r9[] = { + "PUSH_UPDATE,," +}; + + +const char *msg0 = "redirect-gateway local,route 192.168.1.0 255.255.255.0"; +const char *msg1 = "-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8,redirect-gateway local,route 192.168.1.0 255.255.255.0"; +const char *msg2 = ""; +const char *msg3 = ",,"; +const char *msg4 = "-dhcp-option, blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf, akakakakakakakakakakakaf,dhcp-option DNS 8.8.8.8, redirect-gateway local, route 192.168.1.0 255.255.255.0,"; +const char *msg5 = ",-dhcp-option, blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf, akakakakakakakakakakakaf,dhcp-option DNS 8.8.8.8, redirect-gateway local, route 192.168.1.0 255.255.255.0"; +const char *msg6 = "-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8, redirect-gateway 10.10.10.10,, route 192.168.1.0 255.255.255.0,"; +const char *msg7 = ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; +const char *msg8 = "-dhcp-option,blablalalalalalalalalalalalalf, lalalalalalalalalalalalalalaf, akakakakakakakakakakakaf, dhcp-option DNS 8.8.8.8,redirect-gateway\n local,route 192.168.1.0 255.255.255.0\n\n\n"; +const char *msg9 = ","; +const char *msg10 = "VoilĂ ! In view, a humble vaudevillian veteran cast vicariously as both victim and villain by the vicissitudes of Fate. This visage no mere veneer of vanity is a vestige of the vox populi now vacant vanished. However this valorous visitation of a by-gone vexation stands vivified and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive not in vain for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily this vichyssoise of verbiage veers most verbose so let me simply add that it is my very good honor to meet you and you may call me V."; + +#define PUSH_BUNDLE_SIZE_TEST 184 + +static void +test_send_push_msg0(void **state) +{ + i = 0; + res = r0; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg0, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} +static void +test_send_push_msg1(void **state) +{ + i = 0; + res = r1; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg1, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg2(void **state) +{ + i = 0; + res = NULL; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg2, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), -EINVAL); +} + +static void +test_send_push_msg3(void **state) +{ + i = 0; + res = r3; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg3, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg4(void **state) +{ + i = 0; + res = r4; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg4, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg5(void **state) +{ + i = 0; + res = r5; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg5, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg6(void **state) +{ + i = 0; + res = r6; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg6, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg7(void **state) +{ + i = 0; + res = r7; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg7, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg8(void **state) +{ + i = 0; + res = r8; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg8, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg9(void **state) +{ + i = 0; + res = r9; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg9, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), 1); +} + +static void +test_send_push_msg10(void **state) +{ + i = 0; + res = NULL; + struct multi_context *m = *state; + const unsigned long cid = 0; + assert_int_equal(send_push_update(m, &cid, msg10, UPT_BY_CID, PUSH_BUNDLE_SIZE_TEST), -EINVAL); +} + +#undef PUSH_BUNDLE_SIZE_TEST + +static int +setup2(void **state) +{ + struct multi_context *m = calloc(1, sizeof(struct multi_context)); + m->instances = calloc(1, sizeof(struct multi_instance *)); + struct multi_instance *mi = calloc(1, sizeof(struct multi_instance)); + *(m->instances) = mi; + *state = m; + return 0; +} + +static int +teardown2(void **state) +{ + struct multi_context *m = *state; + free(*(m->instances)); + free(m->instances); + free(m); + return 0; +} +#endif /* ifdef ENABLE_MANAGEMENT */ + static int setup(void **state) { @@ -238,7 +468,20 @@ cmocka_unit_test_setup_teardown(test_incoming_push_message_1, setup, teardown), cmocka_unit_test_setup_teardown(test_incoming_push_message_bad_format, setup, teardown), cmocka_unit_test_setup_teardown(test_incoming_push_message_mix, setup, teardown), - cmocka_unit_test_setup_teardown(test_incoming_push_message_mix2, setup, teardown) + cmocka_unit_test_setup_teardown(test_incoming_push_message_mix2, setup, teardown), +#ifdef ENABLE_MANAGEMENT + cmocka_unit_test_setup_teardown(test_send_push_msg0, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg1, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg2, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg3, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg4, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg5, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg6, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg7, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg8, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg9, setup2, teardown2), + cmocka_unit_test_setup_teardown(test_send_push_msg10, setup2, teardown2) +#endif }; return cmocka_run_group_tests(tests, NULL, NULL);