diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/ntp_signd | |
parent | Initial commit. (diff) | |
download | samba-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/README | 7 | ||||
-rw-r--r-- | source4/ntp_signd/ntp-dev-4.2.5p125.diff | 579 | ||||
-rw-r--r-- | source4/ntp_signd/ntp_signd.c | 590 | ||||
-rw-r--r-- | source4/ntp_signd/wscript_build | 11 |
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() + ) + |