1258945Sroberto/* Copyright 2008, Red Hat, Inc.
2258945Sroberto   Copyright 2008, Andrew Tridgell.
3258945Sroberto   Licenced under the same terms as NTP itself.
4258945Sroberto */
5258945Sroberto#ifdef HAVE_CONFIG_H
6258945Sroberto#include <config.h>
7258945Sroberto#endif
8258945Sroberto
9258945Sroberto#ifdef HAVE_NTP_SIGND
10258945Sroberto
11258945Sroberto#include "ntpd.h"
12258945Sroberto#include "ntp_io.h"
13258945Sroberto#include "ntp_stdlib.h"
14258945Sroberto#include "ntp_unixtime.h"
15258945Sroberto#include "ntp_control.h"
16258945Sroberto#include "ntp_string.h"
17258945Sroberto
18258945Sroberto#include <stdio.h>
19258945Sroberto#include <stddef.h>
20258945Sroberto#ifdef HAVE_LIBSCF_H
21258945Sroberto#include <libscf.h>
22258945Sroberto#include <unistd.h>
23258945Sroberto#endif /* HAVE_LIBSCF_H */
24258945Sroberto
25258945Sroberto#include <sys/un.h>
26258945Sroberto
27258945Sroberto/* socket routines by tridge - from junkcode.samba.org */
28258945Sroberto
29258945Sroberto/*
30258945Sroberto  connect to a unix domain socket
31258945Sroberto*/
32258945Srobertostatic int
33258945Srobertoux_socket_connect(const char *name)
34258945Sroberto{
35258945Sroberto	int fd;
36280849Scy	struct sockaddr_un addr;
37258945Sroberto	if (!name) {
38258945Sroberto		return -1;
39258945Sroberto	}
40258945Sroberto
41280849Scy	ZERO(addr);
42280849Scy	addr.sun_family = AF_UNIX;
43280849Scy	strlcpy(addr.sun_path, name, sizeof(addr.sun_path));
44258945Sroberto
45258945Sroberto	fd = socket(AF_UNIX, SOCK_STREAM, 0);
46258945Sroberto	if (fd == -1) {
47258945Sroberto		return -1;
48258945Sroberto	}
49258945Sroberto
50258945Sroberto	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
51258945Sroberto		close(fd);
52258945Sroberto		return -1;
53258945Sroberto	}
54258945Sroberto
55258945Sroberto	return fd;
56258945Sroberto}
57258945Sroberto
58258945Sroberto
59258945Sroberto/*
60258945Sroberto  keep writing until its all sent
61258945Sroberto*/
62258945Srobertostatic int
63258945Srobertowrite_all(int fd, const void *buf, size_t len)
64258945Sroberto{
65258945Sroberto	size_t total = 0;
66258945Sroberto	while (len) {
67258945Sroberto		int n = write(fd, buf, len);
68258945Sroberto		if (n <= 0) return total;
69293423Sdelphij		buf = n + (const char *)buf;
70258945Sroberto		len -= n;
71258945Sroberto		total += n;
72258945Sroberto	}
73258945Sroberto	return total;
74258945Sroberto}
75258945Sroberto
76258945Sroberto/*
77258945Sroberto  keep reading until its all read
78258945Sroberto*/
79258945Srobertostatic int
80258945Srobertoread_all(int fd, void *buf, size_t len)
81258945Sroberto{
82258945Sroberto	size_t total = 0;
83258945Sroberto	while (len) {
84258945Sroberto		int n = read(fd, buf, len);
85258945Sroberto		if (n <= 0) return total;
86258945Sroberto		buf = n + (char *)buf;
87258945Sroberto		len -= n;
88258945Sroberto		total += n;
89258945Sroberto	}
90258945Sroberto	return total;
91258945Sroberto}
92258945Sroberto
93258945Sroberto/*
94258945Sroberto  send a packet in length prefix format
95258945Sroberto*/
96258945Srobertostatic int
97258945Srobertosend_packet(int fd, const char *buf, uint32_t len)
98258945Sroberto{
99258945Sroberto	uint32_t net_len = htonl(len);
100258945Sroberto	if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
101258945Sroberto	if (write_all(fd, buf, len) != len) return -1;
102258945Sroberto	return 0;
103258945Sroberto}
104258945Sroberto
105258945Sroberto/*
106258945Sroberto  receive a packet in length prefix format
107258945Sroberto*/
108258945Srobertostatic int
109258945Srobertorecv_packet(int fd, char **buf, uint32_t *len)
110258945Sroberto{
111258945Sroberto	if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
112258945Sroberto	*len = ntohl(*len);
113293423Sdelphij	*buf = emalloc(*len);
114258945Sroberto	if (read_all(fd, *buf, *len) != *len) {
115258945Sroberto		free(*buf);
116293423Sdelphij		*buf = NULL;
117258945Sroberto		return -1;
118258945Sroberto	}
119258945Sroberto	return 0;
120258945Sroberto}
121258945Sroberto
122258945Srobertovoid
123258945Srobertosend_via_ntp_signd(
124258945Sroberto	struct recvbuf *rbufp,	/* receive packet pointer */
125258945Sroberto	int	xmode,
126258945Sroberto	keyid_t	xkeyid,
127258945Sroberto	int flags,
128258945Sroberto	struct pkt  *xpkt
129258945Sroberto	)
130258945Sroberto{
131258945Sroberto
132258945Sroberto	/* We are here because it was detected that the client
133258945Sroberto	 * sent an all-zero signature, and we therefore know
134258945Sroberto	 * it's windows trying to talk to an AD server
135258945Sroberto	 *
136258945Sroberto	 * Because we don't want to dive into Samba's secrets
137258945Sroberto	 * database just to find the long-term kerberos key
138258945Sroberto	 * that is re-used as the NTP key, we instead hand the
139258945Sroberto	 * packet over to Samba to sign, and return to us.
140258945Sroberto	 *
141258945Sroberto	 * The signing method Samba will use is described by
142258945Sroberto	 * Microsoft in MS-SNTP, found here:
143258945Sroberto	 * http://msdn.microsoft.com/en-us/library/cc212930.aspx
144258945Sroberto	 */
145258945Sroberto
146258945Sroberto	int fd, sendlen;
147258945Sroberto	struct samba_key_in {
148258945Sroberto		uint32_t version;
149258945Sroberto		uint32_t op;
150258945Sroberto		uint32_t packet_id;
151258945Sroberto		uint32_t key_id_le;
152258945Sroberto		struct pkt pkt;
153258945Sroberto	} samba_pkt;
154258945Sroberto
155258945Sroberto	struct samba_key_out {
156258945Sroberto		uint32_t version;
157258945Sroberto		uint32_t op;
158258945Sroberto		uint32_t packet_id;
159258945Sroberto		struct pkt pkt;
160258945Sroberto	} samba_reply;
161258945Sroberto
162258945Sroberto	char full_socket[256];
163258945Sroberto
164258945Sroberto	char *reply = NULL;
165258945Sroberto	uint32_t reply_len;
166258945Sroberto
167280849Scy	ZERO(samba_pkt);
168258945Sroberto	samba_pkt.op = 0; /* Sign message */
169258945Sroberto	/* This will be echoed into the reply - a different
170258945Sroberto	 * impelementation might want multiple packets
171258945Sroberto	 * awaiting signing */
172258945Sroberto
173258945Sroberto	samba_pkt.packet_id = 1;
174258945Sroberto
175258945Sroberto	/* Swap the byte order back - it's actually little
176258945Sroberto	 * endian on the wire, but it was read above as
177258945Sroberto	 * network byte order */
178258945Sroberto	samba_pkt.key_id_le = htonl(xkeyid);
179258945Sroberto	samba_pkt.pkt = *xpkt;
180258945Sroberto
181258945Sroberto	snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
182258945Sroberto
183258945Sroberto	fd = ux_socket_connect(full_socket);
184258945Sroberto	/* Only continue with this if we can talk to Samba */
185258945Sroberto	if (fd != -1) {
186258945Sroberto		/* Send old packet to Samba, expect response */
187258945Sroberto		/* Packet to Samba is quite simple:
188258945Sroberto		   All values BIG endian except key ID as noted
189258945Sroberto		   [packet size as BE] - 4 bytes
190258945Sroberto		   [protocol version (0)] - 4 bytes
191258945Sroberto		   [packet ID] - 4 bytes
192258945Sroberto		   [operation (sign message=0)] - 4 bytes
193258945Sroberto		   [key id] - LITTLE endian (as on wire) - 4 bytes
194258945Sroberto		   [message to sign] - as marshalled, without signature
195258945Sroberto		*/
196258945Sroberto
197258945Sroberto		if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
198258945Sroberto			/* Huh?  could not talk to Samba... */
199258945Sroberto			close(fd);
200258945Sroberto			return;
201258945Sroberto		}
202258945Sroberto
203258945Sroberto		if (recv_packet(fd, &reply, &reply_len) != 0) {
204258945Sroberto			if (reply) {
205258945Sroberto				free(reply);
206258945Sroberto			}
207258945Sroberto			close(fd);
208258945Sroberto			return;
209258945Sroberto		}
210258945Sroberto		/* Return packet is also simple:
211258945Sroberto		   [packet size] - network byte order - 4 bytes
212258945Sroberto		   [protocol version (0)] network byte order - - 4 bytes
213258945Sroberto		   [operation (signed success=3, failure=4)] network byte order - - 4 byte
214258945Sroberto		   (optional) [signed message] - as provided before, with signature appended
215258945Sroberto		*/
216258945Sroberto
217258945Sroberto		if (reply_len <= sizeof(samba_reply)) {
218258945Sroberto			memcpy(&samba_reply, reply, reply_len);
219258945Sroberto			if (ntohl(samba_reply.op) == 3 && reply_len >  offsetof(struct samba_key_out, pkt)) {
220258945Sroberto				sendlen = reply_len - offsetof(struct samba_key_out, pkt);
221258945Sroberto				xpkt = &samba_reply.pkt;
222258945Sroberto				sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
223258945Sroberto#ifdef DEBUG
224258945Sroberto				if (debug)
225258945Sroberto					printf(
226258945Sroberto						"transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
227258945Sroberto						current_time, ntoa(&rbufp->dstadr->sin),
228258945Sroberto						ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
229258945Sroberto#endif
230258945Sroberto			}
231258945Sroberto		}
232258945Sroberto
233258945Sroberto		if (reply) {
234258945Sroberto			free(reply);
235258945Sroberto		}
236258945Sroberto		close(fd);
237258945Sroberto
238258945Sroberto	}
239258945Sroberto}
240258945Sroberto#endif
241