summaryrefslogtreecommitdiffstats
path: root/source4/ntp_signd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/ntp_signd
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/ntp_signd')
-rw-r--r--source4/ntp_signd/README7
-rw-r--r--source4/ntp_signd/ntp-dev-4.2.5p125.diff579
-rw-r--r--source4/ntp_signd/ntp_signd.c590
-rw-r--r--source4/ntp_signd/wscript_build11
4 files changed, 1187 insertions, 0 deletions
diff --git a/source4/ntp_signd/README b/source4/ntp_signd/README
new file mode 100644
index 0000000..585459b
--- /dev/null
+++ b/source4/ntp_signd/README
@@ -0,0 +1,7 @@
+Here are some pointers to the needed ntp version.
+
+https://support.ntp.org/bugs/show_bug.cgi?id=1028
+
+The patch against ntp-dev-4.2.5p125
+https://support.ntp.org/bugs/attachment.cgi?id=457
+
diff --git a/source4/ntp_signd/ntp-dev-4.2.5p125.diff b/source4/ntp_signd/ntp-dev-4.2.5p125.diff
new file mode 100644
index 0000000..40669fb
--- /dev/null
+++ b/source4/ntp_signd/ntp-dev-4.2.5p125.diff
@@ -0,0 +1,579 @@
+Only in ntp-samba: autom4te.cache
+Only in ntp-samba: config.h
+Only in ntp-samba: config.log
+Only in ntp-samba: config.status
+Only in ntp-samba/ElectricFence: .deps
+Only in ntp-samba/ElectricFence: Makefile
+Only in ntp-samba: .gcc-warning
+Only in ntp-samba/include/isc: Makefile
+Only in ntp-samba/include: Makefile
+diff -ur ntp-dev-4.2.5p125/include/ntp_config.h ntp-samba/include/ntp_config.h
+--- ntp-dev-4.2.5p125/include/ntp_config.h 2008-07-17 07:20:58.000000000 +1000
++++ ntp-samba/include/ntp_config.h 2008-08-28 21:59:06.000000000 +1000
+@@ -92,6 +92,7 @@
+ int requested_key;
+ int revoke;
+ queue *trusted_key_list;
++ char *ntp_signd_socket;
+ };
+
+ struct filegen_node {
+diff -ur ntp-dev-4.2.5p125/include/ntpd.h ntp-samba/include/ntpd.h
+--- ntp-dev-4.2.5p125/include/ntpd.h 2008-05-18 21:11:28.000000000 +1000
++++ ntp-samba/include/ntpd.h 2008-08-28 21:59:06.000000000 +1000
+@@ -259,6 +259,8 @@
+ extern int config_priority;
+ #endif
+
++extern char const *ntp_signd_socket;
++
+ /* ntp_control.c */
+ extern int num_ctl_traps;
+ extern keyid_t ctl_auth_keyid; /* keyid used for authenticating write requests */
+@@ -471,3 +473,15 @@
+ extern struct refclock *refclock_conf[]; /* refclock configuration table */
+ extern u_char num_refclock_conf;
+ #endif
++
++/* ntp_signd.c */
++#ifdef HAVE_NTP_SIGND
++extern void
++send_via_ntp_signd(
++ struct recvbuf *rbufp, /* receive packet pointer */
++ int xmode,
++ keyid_t xkeyid,
++ int flags,
++ struct pkt *xpkt
++ );
++#endif
+diff -ur ntp-dev-4.2.5p125/include/ntp.h ntp-samba/include/ntp.h
+--- ntp-dev-4.2.5p125/include/ntp.h 2008-08-10 22:37:56.000000000 +1000
++++ ntp-samba/include/ntp.h 2008-08-28 21:59:06.000000000 +1000
+@@ -447,6 +447,7 @@
+ #ifdef OPENSSL
+ #define FLAG_ASSOC 0x4000 /* autokey request */
+ #endif /* OPENSSL */
++#define FLAG_ADKEY 0x00010000 /* Authenticated (or wants reply to be authenticated) using AD authentication */
+
+ /*
+ * Definitions for the clear() routine. We use memset() to clear
+Only in ntp-samba/include: ntp.h.orig
+Only in ntp-samba: libtool
+Only in ntp-samba: Makefile
+diff -ur ntp-dev-4.2.5p125/ntpd/Makefile.am ntp-samba/ntpd/Makefile.am
+--- ntp-dev-4.2.5p125/ntpd/Makefile.am 2008-05-18 21:11:29.000000000 +1000
++++ ntp-samba/ntpd/Makefile.am 2008-08-28 21:59:06.000000000 +1000
+@@ -65,7 +65,7 @@
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+- ntp_restrict.c ntp_timer.c ntp_util.c \
++ ntp_restrict.c ntp_timer.c ntp_util.c ntp_signd.c \
+ ppsapi_timepps.h \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_config.c ntp-samba/ntpd/ntp_config.c
+--- ntp-dev-4.2.5p125/ntpd/ntp_config.c 2008-08-10 22:37:54.000000000 +1000
++++ ntp-samba/ntpd/ntp_config.c 2008-08-28 22:03:52.000000000 +1000
+@@ -148,6 +148,7 @@
+ #endif
+
+ const char *config_file;
++const char *ntp_signd_socket;
+ #ifdef HAVE_NETINFO
+ struct netinfo_config_state *config_netinfo = NULL;
+ int check_netinfo = 1;
+@@ -276,6 +277,11 @@
+ my_config.auth.crypto_cmd_list = NULL;
+ my_config.auth.keys = NULL;
+ my_config.auth.keysdir = NULL;
++#ifdef NTP_SIGND_PATH
++ my_config.auth.ntp_signd_socket = NTP_SIGND_PATH;
++#else
++ my_config.auth.ntp_signd_socket = NULL;
++#endif
+ my_config.auth.requested_key = 0;
+ my_config.auth.revoke = 0;
+ my_config.auth.trusted_key_list = NULL;
+@@ -795,6 +801,7 @@
+ { "crypto", T_Crypto, NO_ARG },
+ { "keys", T_Keys, SINGLE_ARG },
+ { "keysdir", T_Keysdir, SINGLE_ARG },
++ { "ntpsigndsocket", T_NtpSignDsocket, SINGLE_ARG },
+ { "requestkey", T_Requestkey, NO_ARG },
+ { "revoke", T_Revoke, NO_ARG },
+ { "trustedkey", T_Trustedkey, NO_ARG },
+@@ -1000,6 +1007,10 @@
+ if (my_config.auth.keysdir)
+ keysdir = my_config.auth.keysdir;
+
++ /* ntp_signd_socket Command */
++ if (my_config.auth.ntp_signd_socket)
++ ntp_signd_socket = my_config.auth.ntp_signd_socket;
++
+ #ifdef OPENSSL
+ if (cryptosw) {
+ crypto_setup();
+Only in ntp-samba/ntpd: ntp_config.c~
+Only in ntp-samba/ntpd: ntp_config.c.orig
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_parser.y ntp-samba/ntpd/ntp_parser.y
+--- ntp-dev-4.2.5p125/ntpd/ntp_parser.y 2008-07-17 07:21:06.000000000 +1000
++++ ntp-samba/ntpd/ntp_parser.y 2008-08-28 21:59:06.000000000 +1000
+@@ -155,6 +155,7 @@
+ %token T_Novolley
+ %token T_Ntp
+ %token T_Ntpport
++%token T_NtpSignDsocket
+ %token T_Orphan
+ %token T_Panic
+ %token T_Peer
+@@ -432,6 +433,8 @@
+ { my_config.auth.requested_key = $2; }
+ | T_Trustedkey integer_list
+ { my_config.auth.trusted_key_list = $2; }
++ | T_NtpSignDsocket T_String
++ { my_config.auth.ntp_signd_socket = $2; }
+ ;
+
+ crypto_command_line
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_proto.c ntp-samba/ntpd/ntp_proto.c
+--- ntp-dev-4.2.5p125/ntpd/ntp_proto.c 2008-07-17 07:21:02.000000000 +1000
++++ ntp-samba/ntpd/ntp_proto.c 2008-08-28 21:59:06.000000000 +1000
+@@ -128,7 +128,7 @@
+ static void clock_combine (struct peer **, int);
+ static void peer_xmit (struct peer *);
+ static void fast_xmit (struct recvbuf *, int, keyid_t,
+- char *);
++ char *, int);
+ static void clock_update (struct peer *);
+ static int default_get_precision (void);
+ static int peer_unfit (struct peer *);
+@@ -311,6 +311,7 @@
+ int authlen; /* offset of MAC field */
+ int is_authentic = 0; /* cryptosum ok */
+ int retcode = AM_NOMATCH; /* match code */
++ int flags = 0; /* flags with details about the authentication */
+ keyid_t skeyid = 0; /* key IDs */
+ u_int32 opcode = 0; /* extension field opcode */
+ struct sockaddr_storage *dstadr_sin; /* active runway */
+@@ -324,6 +325,8 @@
+ keyid_t pkeyid = 0, tkeyid = 0; /* key IDs */
+ #endif /* OPENSSL */
+
++ static unsigned char zero_key[16];
++
+ /*
+ * Monitor the packet and get restrictions. Note that the packet
+ * length for control and private mode packets must be checked
+@@ -480,9 +483,9 @@
+ return; /* rate exceeded */
+
+ if (hismode == MODE_CLIENT)
+- fast_xmit(rbufp, MODE_SERVER, skeyid, "RATE");
++ fast_xmit(rbufp, MODE_SERVER, skeyid, "RATE", 0);
+ else
+- fast_xmit(rbufp, MODE_ACTIVE, skeyid, "RATE");
++ fast_xmit(rbufp, MODE_ACTIVE, skeyid, "RATE", 0);
+ return; /* rate exceeded */
+ }
+
+@@ -535,6 +538,7 @@
+ * is zero, acceptable outcomes of y are NONE and OK. If x is
+ * one, the only acceptable outcome of y is OK.
+ */
++
+ if (has_mac == 0) {
+ is_authentic = AUTH_NONE; /* not required */
+ #ifdef DEBUG
+@@ -555,6 +559,25 @@
+ stoa(&rbufp->recv_srcadr), hismode, skeyid,
+ authlen + has_mac, is_authentic);
+ #endif
++
++ /* If the signature is 20 bytes long, the last 16 of
++ * which are zero, then this is a Microsoft client
++ * wanting AD-style authentication of the server's
++ * reply.
++ *
++ * This is described in Microsoft's WSPP docs, in MS-SNTP:
++ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
++ */
++ } else if (has_mac == MAX_MAC_LEN
++ && (retcode == AM_FXMIT || retcode == AM_NEWPASS)
++ && (memcmp(zero_key, (char *)pkt + authlen + 4, MAX_MAC_LEN - 4) == 0)) {
++
++ /* Don't try to verify the zeros, just set a
++ * flag and otherwise pretend we never saw the signature */
++ is_authentic = AUTH_NONE;
++
++ flags = FLAG_ADKEY;
++
+ } else {
+ #ifdef OPENSSL
+ /*
+@@ -696,9 +719,9 @@
+ if (AUTH(restrict_mask & RES_DONTTRUST,
+ is_authentic)) {
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+- NULL);
++ NULL, flags);
+ } else if (is_authentic == AUTH_ERROR) {
+- fast_xmit(rbufp, MODE_SERVER, 0, NULL);
++ fast_xmit(rbufp, MODE_SERVER, 0, NULL, 0);
+ sys_badauth++;
+ } else {
+ sys_restricted++;
+@@ -733,7 +756,7 @@
+ * crypto-NAK, as that would not be useful.
+ */
+ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
+- fast_xmit(rbufp, MODE_SERVER, skeyid, NULL);
++ fast_xmit(rbufp, MODE_SERVER, skeyid, NULL, 0);
+ return; /* hooray */
+
+ /*
+@@ -888,7 +911,7 @@
+ is_authentic)) {
+ #ifdef OPENSSL
+ if (crypto_flags && skeyid > NTP_MAXKEY)
+- fast_xmit(rbufp, MODE_ACTIVE, 0, NULL);
++ fast_xmit(rbufp, MODE_ACTIVE, 0, NULL, 0);
+ #endif /* OPENSSL */
+ sys_restricted++;
+ return; /* access denied */
+@@ -904,7 +927,7 @@
+ * This is for drat broken Windows clients. See
+ * Microsoft KB 875424 for preferred workaround.
+ */
+- fast_xmit(rbufp, MODE_PASSIVE, skeyid, NULL);
++ fast_xmit(rbufp, MODE_PASSIVE, skeyid, NULL, flags);
+ #else /* WINTIME */
+ sys_restricted++;
+ #endif /* WINTIME */
+@@ -938,6 +961,7 @@
+ }
+ break;
+
++
+ /*
+ * Process regular packet. Nothing special.
+ */
+@@ -1090,7 +1114,7 @@
+ peer->flash |= TEST5; /* bad auth */
+ peer->badauth++;
+ if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE)
+- fast_xmit(rbufp, MODE_ACTIVE, 0, NULL);
++ fast_xmit(rbufp, MODE_ACTIVE, 0, NULL, 0);
+ if (peer->flags & FLAG_PREEMPT) {
+ unpeer(peer);
+ return;
+@@ -3159,7 +3183,8 @@
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* receive mode */
+ keyid_t xkeyid, /* transmit key ID */
+- char *mask /* kiss code */
++ char *mask, /* kiss code */
++ int flags /* Flags to indicate signing behaviour */
+ )
+ {
+ struct pkt xpkt; /* transmit packet structure */
+@@ -3220,6 +3245,19 @@
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+ }
+
++ if (flags & FLAG_ADKEY) {
++#ifdef HAVE_NTP_SIGND
++ get_systime(&xmt_tx);
++ if (mask == NULL) {
++ HTONL_FP(&xmt_tx, &xpkt.xmt);
++ }
++ send_via_ntp_signd(rbufp, xmode, xkeyid, flags, &xpkt);
++#endif
++ /* If we don't have the support, drop the packet on the floor.
++ An all zero sig is compleatly bogus anyway */
++ return;
++ }
++
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+@@ -3252,7 +3290,7 @@
+ * source-destination-key ID combination.
+ */
+ #ifdef OPENSSL
+- if (xkeyid > NTP_MAXKEY) {
++ if (!(flags & FLAG_ADKEY) && (xkeyid > NTP_MAXKEY)) {
+ keyid_t cookie;
+
+ /*
+@@ -3284,8 +3322,10 @@
+ if (mask == NULL) {
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ }
++
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ sendlen += authlen;
++
+ #ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+Only in ntp-samba/ntpd: ntp_signd.c
+Only in ntp-dev-4.2.5p125/ntpdc: nl.pl
+Only in ntp-samba/scripts: calc_tickadj
+Only in ntp-samba/scripts: checktime
+Only in ntp-samba/scripts: freq_adj
+Only in ntp-samba/scripts: html2man
+Only in ntp-samba/scripts: Makefile
+Only in ntp-samba/scripts: mkver
+Only in ntp-samba/scripts: ntpsweep
+Only in ntp-samba/scripts: ntptrace
+Only in ntp-samba/scripts: ntpver
+Only in ntp-samba/scripts: ntp-wait
+Only in ntp-samba/scripts: plot_summary
+Only in ntp-samba/scripts: summary
+Only in ntp-samba: stamp-h1
+--- /dev/null 2008-08-25 07:28:22.036002925 +1000
++++ ntp-samba/ntpd/ntp_signd.c 2008-08-28 21:59:06.000000000 +1000
+@@ -0,0 +1,242 @@
++/* Copyright 2008, Red Hat, Inc.
++ Copyright 2008, Andrew Tridgell.
++ Licenced under the same terms as NTP itself.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#ifdef HAVE_NTP_SIGND
++
++#include "ntpd.h"
++#include "ntp_io.h"
++#include "ntp_stdlib.h"
++#include "ntp_unixtime.h"
++#include "ntp_control.h"
++#include "ntp_string.h"
++
++#include <stdio.h>
++#include <stddef.h>
++#ifdef HAVE_LIBSCF_H
++#include <libscf.h>
++#include <unistd.h>
++#endif /* HAVE_LIBSCF_H */
++
++#include <sys/un.h>
++
++/* socket routines by tridge - from junkcode.samba.org */
++
++/*
++ connect to a unix domain socket
++*/
++static int
++ux_socket_connect(const char *name)
++{
++ int fd;
++ struct sockaddr_un addr;
++ if (!name) {
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sun_family = AF_UNIX;
++ strncpy(addr.sun_path, name, sizeof(addr.sun_path));
++
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1) {
++ return -1;
++ }
++
++ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
++ close(fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++
++/*
++ keep writing until its all sent
++*/
++static int
++write_all(int fd, const void *buf, size_t len)
++{
++ size_t total = 0;
++ while (len) {
++ int n = write(fd, buf, len);
++ if (n <= 0) return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ keep reading until its all read
++*/
++static int
++read_all(int fd, void *buf, size_t len)
++{
++ size_t total = 0;
++ while (len) {
++ int n = read(fd, buf, len);
++ if (n <= 0) return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ send a packet in length prefix format
++*/
++static int
++send_packet(int fd, const char *buf, uint32_t len)
++{
++ uint32_t net_len = htonl(len);
++ if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
++ if (write_all(fd, buf, len) != len) return -1;
++ return 0;
++}
++
++/*
++ receive a packet in length prefix format
++*/
++static int
++recv_packet(int fd, char **buf, uint32_t *len)
++{
++ if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
++ *len = ntohl(*len);
++ (*buf) = malloc(*len);
++ if (!*buf) {
++ return -1;
++ }
++ if (read_all(fd, *buf, *len) != *len) {
++ free(*buf);
++ return -1;
++ }
++ return 0;
++}
++
++void
++send_via_ntp_signd(
++ struct recvbuf *rbufp, /* receive packet pointer */
++ int xmode,
++ keyid_t xkeyid,
++ int flags,
++ struct pkt *xpkt
++ )
++{
++
++ /* We are here because it was detected that the client
++ * sent an all-zero signature, and we therefore know
++ * it's windows trying to talk to an AD server
++ *
++ * Because we don't want to dive into Samba's secrets
++ * database just to find the long-term kerberos key
++ * that is re-used as the NTP key, we instead hand the
++ * packet over to Samba to sign, and return to us.
++ *
++ * The signing method Samba will use is described by
++ * Microsoft in MS-SNTP, found here:
++ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
++ */
++
++ int fd, sendlen;
++ struct samba_key_in {
++ uint32_t version;
++ uint32_t op;
++ uint32_t packet_id;
++ uint32_t key_id_le;
++ struct pkt pkt;
++ } samba_pkt;
++
++ struct samba_key_out {
++ uint32_t version;
++ uint32_t op;
++ uint32_t packet_id;
++ struct pkt pkt;
++ } samba_reply;
++
++ char full_socket[256];
++
++ char *reply = NULL;
++ uint32_t reply_len;
++
++ memset(&samba_pkt, 0, sizeof(samba_pkt));
++ samba_pkt.op = 0; /* Sign message */
++ /* This will be echoed into the reply - a different
++ * impelementation might want multiple packets
++ * awaiting signing */
++
++ samba_pkt.packet_id = 1;
++
++ /* Swap the byte order back - it's actually little
++ * endian on the wire, but it was read above as
++ * network byte order */
++ samba_pkt.key_id_le = htonl(xkeyid);
++ samba_pkt.pkt = *xpkt;
++
++ snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
++
++ fd = ux_socket_connect(full_socket);
++ /* Only continue with this if we can talk to Samba */
++ if (fd != -1) {
++ /* Send old packet to Samba, expect response */
++ /* Packet to Samba is quite simple:
++ All values BIG endian except key ID as noted
++ [packet size as BE] - 4 bytes
++ [protocol version (0)] - 4 bytes
++ [packet ID] - 4 bytes
++ [operation (sign message=0)] - 4 bytes
++ [key id] - LITTLE endian (as on wire) - 4 bytes
++ [message to sign] - as marshalled, without signature
++ */
++
++ if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
++ /* Huh? could not talk to Samba... */
++ close(fd);
++ return;
++ }
++
++ if (recv_packet(fd, &reply, &reply_len) != 0) {
++ if (reply) {
++ free(reply);
++ }
++ close(fd);
++ return;
++ }
++ /* Return packet is also simple:
++ [packet size] - network byte order - 4 bytes
++ [protocol version (0)] network byte order - - 4 bytes
++ [operation (signed success=3, failure=4)] network byte order - - 4 byte
++ (optional) [signed message] - as provided before, with signature appended
++ */
++
++ if (reply_len <= sizeof(samba_reply)) {
++ memcpy(&samba_reply, reply, reply_len);
++ if (ntohl(samba_reply.op) == 3 && reply_len > offsetof(struct samba_key_out, pkt)) {
++ sendlen = reply_len - offsetof(struct samba_key_out, pkt);
++ xpkt = &samba_reply.pkt;
++ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
++#ifdef DEBUG
++ if (debug)
++ printf(
++ "transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
++ current_time, ntoa(&rbufp->dstadr->sin),
++ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
++#endif
++ }
++ }
++
++ if (reply) {
++ free(reply);
++ }
++ close(fd);
++
++ }
++}
++#endif
diff --git a/source4/ntp_signd/ntp_signd.c b/source4/ntp_signd/ntp_signd.c
new file mode 100644
index 0000000..eab70f1
--- /dev/null
+++ b/source4/ntp_signd/ntp_signd.c
@@ -0,0 +1,590 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTP packet signing server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "samba/service_task.h"
+#include "samba/service.h"
+#include "samba/service_stream.h"
+#include "samba/process_model.h"
+#include "lib/stream/packet.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "librpc/gen_ndr/ndr_ntp_signd.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "system/network.h"
+#include "system/passwd.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+NTSTATUS server_service_ntp_signd_init(TALLOC_CTX *);
+
+/*
+ top level context structure for the ntp_signd server
+*/
+struct ntp_signd_server {
+ struct task_server *task;
+ struct ldb_context *samdb;
+};
+
+/*
+ state of an open connection
+*/
+struct ntp_signd_connection {
+ /* stream connection we belong to */
+ struct stream_connection *conn;
+
+ /* the ntp_signd_server the connection belongs to */
+ struct ntp_signd_server *ntp_signd;
+
+ struct tstream_context *tstream;
+
+ struct tevent_queue *send_queue;
+};
+
+static void ntp_signd_terminate_connection(struct ntp_signd_connection *ntp_signd_conn, const char *reason)
+{
+ stream_terminate_connection(ntp_signd_conn->conn, reason);
+}
+
+static NTSTATUS signing_failure(struct ntp_signd_connection *ntp_signdconn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *output,
+ uint32_t packet_id)
+{
+ struct signed_reply signed_reply;
+ enum ndr_err_code ndr_err;
+
+ signed_reply.op = SIGNING_FAILURE;
+ signed_reply.packet_id = packet_id;
+ signed_reply.signed_packet = data_blob(NULL, 0);
+
+ ndr_err = ndr_push_struct_blob(output, mem_ctx, &signed_reply,
+ (ndr_push_flags_fn_t)ndr_push_signed_reply);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to push ntp error reply\n"));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ receive a full packet on a NTP_SIGND connection
+*/
+static NTSTATUS ntp_signd_process(struct ntp_signd_connection *ntp_signd_conn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
+ DATA_BLOB *output)
+{
+ const struct dom_sid *domain_sid;
+ struct dom_sid *sid;
+ struct sign_request sign_request;
+ struct signed_reply signed_reply;
+ enum ndr_err_code ndr_err;
+ struct ldb_result *res;
+ const char *attrs[] = { "unicodePwd", "userAccountControl", "cn", NULL };
+ gnutls_hash_hd_t hash_hnd = NULL;
+ struct samr_Password *nt_hash;
+ uint32_t user_account_control;
+ struct dom_sid_buf buf;
+ int ret;
+
+ ndr_err = ndr_pull_struct_blob_all(input, mem_ctx,
+ &sign_request,
+ (ndr_pull_flags_fn_t)ndr_pull_sign_request);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to parse ntp signing request\n"));
+ dump_data(1, input->data, input->length);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* We need to implement 'check signature' and 'request server
+ * to sign' operations at some point */
+ if (sign_request.op != SIGN_TO_CLIENT) {
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ /* We need to implement 'check signature' and 'request server
+ * to sign' operations at some point */
+ if (sign_request.version != NTP_SIGND_PROTOCOL_VERSION_0) {
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ domain_sid = samdb_domain_sid(ntp_signd_conn->ntp_signd->samdb);
+ if (domain_sid == NULL) {
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ /* The top bit is a 'key selector' */
+ sid = dom_sid_add_rid(mem_ctx, domain_sid,
+ sign_request.key_id & 0x7FFFFFFF);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ ret = ldb_search(ntp_signd_conn->ntp_signd->samdb, mem_ctx,
+ &res,
+ ldb_get_default_basedn(ntp_signd_conn->ntp_signd->samdb),
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ "(&(objectSid=%s)(objectClass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2, ("Failed to search for SID %s in SAM for NTP signing: "
+ "%s\n",
+ dom_sid_str_buf(sid, &buf),
+ ldb_errstring(ntp_signd_conn->ntp_signd->samdb)));
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ if (res->count == 0) {
+ DEBUG(2, ("Failed to find SID %s in SAM for NTP signing\n",
+ dom_sid_str_buf(sid, &buf)));
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ } else if (res->count != 1) {
+ DEBUG(1, ("Found SID %s %u times in SAM for NTP signing\n",
+ dom_sid_str_buf(sid, &buf),
+ res->count));
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ user_account_control = ldb_msg_find_attr_as_uint(res->msgs[0],
+ "userAccountControl",
+ 0);
+
+ if (user_account_control & UF_ACCOUNTDISABLE) {
+ DEBUG(1, ("Account %s for SID [%s] is disabled\n",
+ ldb_dn_get_linearized(res->msgs[0]->dn),
+ dom_sid_str_buf(sid, &buf)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!(user_account_control & (UF_INTERDOMAIN_TRUST_ACCOUNT|UF_SERVER_TRUST_ACCOUNT|UF_WORKSTATION_TRUST_ACCOUNT))) {
+ DEBUG(1, ("Account %s for SID [%s] is not a trust account\n",
+ ldb_dn_get_linearized(res->msgs[0]->dn),
+ dom_sid_str_buf(sid, &buf)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ nt_hash = samdb_result_hash(mem_ctx, res->msgs[0], "unicodePwd");
+ if (!nt_hash) {
+ DEBUG(1, ("No unicodePwd found on record of SID %s "
+ "for NTP signing\n",
+ dom_sid_str_buf(sid, &buf)));
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ /* Generate the reply packet */
+ signed_reply.packet_id = sign_request.packet_id;
+ signed_reply.op = SIGNING_SUCCESS;
+ signed_reply.signed_packet = data_blob_talloc(mem_ctx,
+ NULL,
+ sign_request.packet_to_sign.length + 20);
+
+ if (!signed_reply.signed_packet.data) {
+ return signing_failure(ntp_signd_conn,
+ mem_ctx,
+ output,
+ sign_request.packet_id);
+ }
+
+ memcpy(signed_reply.signed_packet.data, sign_request.packet_to_sign.data, sign_request.packet_to_sign.length);
+ SIVAL(signed_reply.signed_packet.data, sign_request.packet_to_sign.length, sign_request.key_id);
+
+ /* Sign the NTP response with the unicodePwd */
+ ret = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (ret < 0) {
+ return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ ret = gnutls_hash(hash_hnd,
+ nt_hash->hash,
+ sizeof(nt_hash->hash));
+ if (ret < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+ ret = gnutls_hash(hash_hnd,
+ sign_request.packet_to_sign.data,
+ sign_request.packet_to_sign.length);
+ if (ret < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return gnutls_error_to_ntstatus(ret, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ gnutls_hash_deinit(hash_hnd,
+ signed_reply.signed_packet.data +
+ sign_request.packet_to_sign.length + 4);
+
+ /* Place it into the packet for the wire */
+ ndr_err = ndr_push_struct_blob(output, mem_ctx, &signed_reply,
+ (ndr_push_flags_fn_t)ndr_push_signed_reply);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to push ntp error reply\n"));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called on a tcp recv
+*/
+static void ntp_signd_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct ntp_signd_connection *ntp_signd_conn = talloc_get_type(conn->private_data,
+ struct ntp_signd_connection);
+ ntp_signd_terminate_connection(ntp_signd_conn,
+ "ntp_signd_recv: called");
+}
+
+/*
+ called when we can write to a connection
+*/
+static void ntp_signd_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct ntp_signd_connection *ntp_signd_conn = talloc_get_type(conn->private_data,
+ struct ntp_signd_connection);
+ /* this should never be triggered! */
+ ntp_signd_terminate_connection(ntp_signd_conn,
+ "ntp_signd_send: called");
+}
+
+struct ntp_signd_call {
+ struct ntp_signd_connection *ntp_signd_conn;
+ DATA_BLOB in;
+ DATA_BLOB out;
+ uint8_t out_hdr[4];
+ struct iovec out_iov[2];
+};
+
+static void ntp_signd_call_writev_done(struct tevent_req *subreq);
+
+static void ntp_signd_call_loop(struct tevent_req *subreq)
+{
+ struct ntp_signd_connection *ntp_signd_conn = tevent_req_callback_data(subreq,
+ struct ntp_signd_connection);
+ struct ntp_signd_call *call;
+ NTSTATUS status;
+
+ call = talloc(ntp_signd_conn, struct ntp_signd_call);
+ if (call == NULL) {
+ ntp_signd_terminate_connection(ntp_signd_conn,
+ "ntp_signd_call_loop: "
+ "no memory for ntp_signd_call");
+ return;
+ }
+ call->ntp_signd_conn = ntp_signd_conn;
+
+ status = tstream_read_pdu_blob_recv(subreq,
+ call,
+ &call->in);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ntp_signd_call_loop: "
+ "tstream_read_pdu_blob_recv() - %s",
+ nt_errstr(status));
+ if (reason == NULL) {
+ reason = nt_errstr(status);
+ }
+
+ ntp_signd_terminate_connection(ntp_signd_conn, reason);
+ return;
+ }
+
+ DEBUG(10,("Received NTP TCP packet of length %lu from %s\n",
+ (long) call->in.length,
+ tsocket_address_string(ntp_signd_conn->conn->remote_address, call)));
+
+ /* skip length header */
+ call->in.data +=4;
+ call->in.length -= 4;
+
+ status = ntp_signd_process(ntp_signd_conn,
+ call,
+ &call->in,
+ &call->out);
+ if (! NT_STATUS_IS_OK(status)) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ntp_signd_process failed: %s",
+ nt_errstr(status));
+ if (reason == NULL) {
+ reason = nt_errstr(status);
+ }
+
+ ntp_signd_terminate_connection(ntp_signd_conn, reason);
+ return;
+ }
+
+ /* First add the length of the out buffer */
+ RSIVAL(call->out_hdr, 0, call->out.length);
+ call->out_iov[0].iov_base = (char *) call->out_hdr;
+ call->out_iov[0].iov_len = 4;
+
+ call->out_iov[1].iov_base = (char *) call->out.data;
+ call->out_iov[1].iov_len = call->out.length;
+
+ subreq = tstream_writev_queue_send(call,
+ ntp_signd_conn->conn->event.ctx,
+ ntp_signd_conn->tstream,
+ ntp_signd_conn->send_queue,
+ call->out_iov, 2);
+ if (subreq == NULL) {
+ ntp_signd_terminate_connection(ntp_signd_conn, "ntp_signd_call_loop: "
+ "no memory for tstream_writev_queue_send");
+ return;
+ }
+
+ tevent_req_set_callback(subreq, ntp_signd_call_writev_done, call);
+
+ /*
+ * The NTP tcp pdu's has the length as 4 byte (initial_read_size),
+ * packet_full_request_u32 provides the pdu length then.
+ */
+ subreq = tstream_read_pdu_blob_send(ntp_signd_conn,
+ ntp_signd_conn->conn->event.ctx,
+ ntp_signd_conn->tstream,
+ 4, /* initial_read_size */
+ packet_full_request_u32,
+ ntp_signd_conn);
+ if (subreq == NULL) {
+ ntp_signd_terminate_connection(ntp_signd_conn, "ntp_signd_call_loop: "
+ "no memory for tstream_read_pdu_blob_send");
+ return;
+ }
+ tevent_req_set_callback(subreq, ntp_signd_call_loop, ntp_signd_conn);
+}
+
+static void ntp_signd_call_writev_done(struct tevent_req *subreq)
+{
+ struct ntp_signd_call *call = tevent_req_callback_data(subreq,
+ struct ntp_signd_call);
+ int sys_errno;
+ int rc;
+
+ rc = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (rc == -1) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ntp_signd_call_writev_done: "
+ "tstream_writev_queue_recv() - %d:%s",
+ sys_errno, strerror(sys_errno));
+ if (!reason) {
+ reason = "ntp_signd_call_writev_done: "
+ "tstream_writev_queue_recv() failed";
+ }
+
+ ntp_signd_terminate_connection(call->ntp_signd_conn, reason);
+ return;
+ }
+
+ /* We don't care about errors */
+
+ talloc_free(call);
+}
+
+/*
+ called when we get a new connection
+*/
+static void ntp_signd_accept(struct stream_connection *conn)
+{
+ struct ntp_signd_server *ntp_signd = talloc_get_type(conn->private_data,
+ struct ntp_signd_server);
+ struct ntp_signd_connection *ntp_signd_conn;
+ struct tevent_req *subreq;
+ int rc;
+
+ ntp_signd_conn = talloc_zero(conn, struct ntp_signd_connection);
+ if (ntp_signd_conn == NULL) {
+ stream_terminate_connection(conn,
+ "ntp_signd_accept: out of memory");
+ return;
+ }
+
+ ntp_signd_conn->send_queue = tevent_queue_create(conn,
+ "ntp_signd_accept");
+ if (ntp_signd_conn->send_queue == NULL) {
+ stream_terminate_connection(conn,
+ "ntp_signd_accept: out of memory");
+ return;
+ }
+
+ TALLOC_FREE(conn->event.fde);
+
+ rc = tstream_bsd_existing_socket(ntp_signd_conn,
+ socket_get_fd(conn->socket),
+ &ntp_signd_conn->tstream);
+ if (rc < 0) {
+ stream_terminate_connection(conn,
+ "ntp_signd_accept: out of memory");
+ return;
+ }
+
+ ntp_signd_conn->conn = conn;
+ ntp_signd_conn->ntp_signd = ntp_signd;
+ conn->private_data = ntp_signd_conn;
+
+ /*
+ * The NTP tcp pdu's has the length as 4 byte (initial_read_size),
+ * packet_full_request_u32 provides the pdu length then.
+ */
+ subreq = tstream_read_pdu_blob_send(ntp_signd_conn,
+ ntp_signd_conn->conn->event.ctx,
+ ntp_signd_conn->tstream,
+ 4, /* initial_read_size */
+ packet_full_request_u32,
+ ntp_signd_conn);
+ if (subreq == NULL) {
+ ntp_signd_terminate_connection(ntp_signd_conn,
+ "ntp_signd_accept: "
+ "no memory for tstream_read_pdu_blob_send");
+ return;
+ }
+ tevent_req_set_callback(subreq, ntp_signd_call_loop, ntp_signd_conn);
+}
+
+static const struct stream_server_ops ntp_signd_stream_ops = {
+ .name = "ntp_signd",
+ .accept_connection = ntp_signd_accept,
+ .recv_handler = ntp_signd_recv,
+ .send_handler = ntp_signd_send
+};
+
+/*
+ startup the ntp_signd task
+*/
+static NTSTATUS ntp_signd_task_init(struct task_server *task)
+{
+ struct ntp_signd_server *ntp_signd;
+ NTSTATUS status;
+
+ const char *address;
+
+ if (!directory_create_or_exist_strict(lpcfg_ntp_signd_socket_directory(task->lp_ctx), geteuid(), 0750)) {
+ char *error = talloc_asprintf(task, "Cannot create NTP signd pipe directory: %s",
+ lpcfg_ntp_signd_socket_directory(task->lp_ctx));
+ task_server_terminate(task,
+ error, true);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ task_server_set_title(task, "task[ntp_signd]");
+
+ ntp_signd = talloc(task, struct ntp_signd_server);
+ if (ntp_signd == NULL) {
+ task_server_terminate(task, "ntp_signd: out of memory", true);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ntp_signd->task = task;
+
+ /* Must be system to get at the password hashes */
+ ntp_signd->samdb = samdb_connect(ntp_signd,
+ task->event_ctx,
+ task->lp_ctx,
+ system_session(task->lp_ctx),
+ NULL,
+ 0);
+ if (ntp_signd->samdb == NULL) {
+ task_server_terminate(task, "ntp_signd failed to open samdb", true);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ address = talloc_asprintf(ntp_signd, "%s/socket", lpcfg_ntp_signd_socket_directory(task->lp_ctx));
+ if (address == NULL) {
+ task_server_terminate(
+ task, "ntp_signd out of memory in talloc_asprintf()", true);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = stream_setup_socket(ntp_signd->task,
+ ntp_signd->task->event_ctx,
+ ntp_signd->task->lp_ctx,
+ task->model_ops,
+ &ntp_signd_stream_ops,
+ "unix", address, NULL,
+ lpcfg_socket_options(ntp_signd->task->lp_ctx),
+ ntp_signd,
+ ntp_signd->task->process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s - %s\n",
+ address, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_ntp_signd_init(TALLOC_CTX *ctx)
+{
+ static const struct service_details details = {
+ .inhibit_fork_on_accept = true,
+ .inhibit_pre_fork = true,
+ .task_init = ntp_signd_task_init,
+ .post_fork = NULL
+ };
+ return register_server_service(ctx, "ntp_signd", &details);
+}
diff --git a/source4/ntp_signd/wscript_build b/source4/ntp_signd/wscript_build
new file mode 100644
index 0000000..ec8a2e5
--- /dev/null
+++ b/source4/ntp_signd/wscript_build
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+bld.SAMBA_MODULE('service_ntp_signd',
+ source='ntp_signd.c',
+ subsystem='service',
+ init_function='server_service_ntp_signd_init',
+ deps='samdb NDR_NTP_SIGND LIBTSOCKET LIBSAMBA_TSOCKET GNUTLS_HELPERS',
+ internal_module=False,
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+