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