ntp_signd.c revision 290000
1/* Copyright 2008, Red Hat, Inc.
2   Copyright 2008, Andrew Tridgell.
3   Licenced under the same terms as NTP itself.
4 */
5#ifdef HAVE_CONFIG_H
6#include <config.h>
7#endif
8
9#ifdef HAVE_NTP_SIGND
10
11#include "ntpd.h"
12#include "ntp_io.h"
13#include "ntp_stdlib.h"
14#include "ntp_unixtime.h"
15#include "ntp_control.h"
16#include "ntp_string.h"
17
18#include <stdio.h>
19#include <stddef.h>
20#ifdef HAVE_LIBSCF_H
21#include <libscf.h>
22#include <unistd.h>
23#endif /* HAVE_LIBSCF_H */
24
25#include <sys/un.h>
26
27/* socket routines by tridge - from junkcode.samba.org */
28
29/*
30  connect to a unix domain socket
31*/
32static int
33ux_socket_connect(const char *name)
34{
35	int fd;
36	struct sockaddr_un addr;
37	if (!name) {
38		return -1;
39	}
40
41	ZERO(addr);
42	addr.sun_family = AF_UNIX;
43	strlcpy(addr.sun_path, name, sizeof(addr.sun_path));
44
45	fd = socket(AF_UNIX, SOCK_STREAM, 0);
46	if (fd == -1) {
47		return -1;
48	}
49
50	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
51		close(fd);
52		return -1;
53	}
54
55	return fd;
56}
57
58
59/*
60  keep writing until its all sent
61*/
62static int
63write_all(int fd, const void *buf, size_t len)
64{
65	size_t total = 0;
66	while (len) {
67		int n = write(fd, buf, len);
68		if (n <= 0) return total;
69		buf = n + (char *)buf;
70		len -= n;
71		total += n;
72	}
73	return total;
74}
75
76/*
77  keep reading until its all read
78*/
79static int
80read_all(int fd, void *buf, size_t len)
81{
82	size_t total = 0;
83	while (len) {
84		int n = read(fd, buf, len);
85		if (n <= 0) return total;
86		buf = n + (char *)buf;
87		len -= n;
88		total += n;
89	}
90	return total;
91}
92
93/*
94  send a packet in length prefix format
95*/
96static int
97send_packet(int fd, const char *buf, uint32_t len)
98{
99	uint32_t net_len = htonl(len);
100	if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
101	if (write_all(fd, buf, len) != len) return -1;
102	return 0;
103}
104
105/*
106  receive a packet in length prefix format
107*/
108static int
109recv_packet(int fd, char **buf, uint32_t *len)
110{
111	if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
112	*len = ntohl(*len);
113	(*buf) = emalloc(*len);
114	if (read_all(fd, *buf, *len) != *len) {
115		free(*buf);
116		return -1;
117	}
118	return 0;
119}
120
121void
122send_via_ntp_signd(
123	struct recvbuf *rbufp,	/* receive packet pointer */
124	int	xmode,
125	keyid_t	xkeyid,
126	int flags,
127	struct pkt  *xpkt
128	)
129{
130
131	/* We are here because it was detected that the client
132	 * sent an all-zero signature, and we therefore know
133	 * it's windows trying to talk to an AD server
134	 *
135	 * Because we don't want to dive into Samba's secrets
136	 * database just to find the long-term kerberos key
137	 * that is re-used as the NTP key, we instead hand the
138	 * packet over to Samba to sign, and return to us.
139	 *
140	 * The signing method Samba will use is described by
141	 * Microsoft in MS-SNTP, found here:
142	 * http://msdn.microsoft.com/en-us/library/cc212930.aspx
143	 */
144
145	int fd, sendlen;
146	struct samba_key_in {
147		uint32_t version;
148		uint32_t op;
149		uint32_t packet_id;
150		uint32_t key_id_le;
151		struct pkt pkt;
152	} samba_pkt;
153
154	struct samba_key_out {
155		uint32_t version;
156		uint32_t op;
157		uint32_t packet_id;
158		struct pkt pkt;
159	} samba_reply;
160
161	char full_socket[256];
162
163	char *reply = NULL;
164	uint32_t reply_len;
165
166	ZERO(samba_pkt);
167	samba_pkt.op = 0; /* Sign message */
168	/* This will be echoed into the reply - a different
169	 * impelementation might want multiple packets
170	 * awaiting signing */
171
172	samba_pkt.packet_id = 1;
173
174	/* Swap the byte order back - it's actually little
175	 * endian on the wire, but it was read above as
176	 * network byte order */
177	samba_pkt.key_id_le = htonl(xkeyid);
178	samba_pkt.pkt = *xpkt;
179
180	snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
181
182	fd = ux_socket_connect(full_socket);
183	/* Only continue with this if we can talk to Samba */
184	if (fd != -1) {
185		/* Send old packet to Samba, expect response */
186		/* Packet to Samba is quite simple:
187		   All values BIG endian except key ID as noted
188		   [packet size as BE] - 4 bytes
189		   [protocol version (0)] - 4 bytes
190		   [packet ID] - 4 bytes
191		   [operation (sign message=0)] - 4 bytes
192		   [key id] - LITTLE endian (as on wire) - 4 bytes
193		   [message to sign] - as marshalled, without signature
194		*/
195
196		if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
197			/* Huh?  could not talk to Samba... */
198			close(fd);
199			return;
200		}
201
202		if (recv_packet(fd, &reply, &reply_len) != 0) {
203			if (reply) {
204				free(reply);
205			}
206			close(fd);
207			return;
208		}
209		/* Return packet is also simple:
210		   [packet size] - network byte order - 4 bytes
211		   [protocol version (0)] network byte order - - 4 bytes
212		   [operation (signed success=3, failure=4)] network byte order - - 4 byte
213		   (optional) [signed message] - as provided before, with signature appended
214		*/
215
216		if (reply_len <= sizeof(samba_reply)) {
217			memcpy(&samba_reply, reply, reply_len);
218			if (ntohl(samba_reply.op) == 3 && reply_len >  offsetof(struct samba_key_out, pkt)) {
219				sendlen = reply_len - offsetof(struct samba_key_out, pkt);
220				xpkt = &samba_reply.pkt;
221				sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
222#ifdef DEBUG
223				if (debug)
224					printf(
225						"transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
226						current_time, ntoa(&rbufp->dstadr->sin),
227						ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
228#endif
229			}
230		}
231
232		if (reply) {
233			free(reply);
234		}
235		close(fd);
236
237	}
238}
239#endif
240