ntp_signd.c revision 280849
1293734Sarybchik/* Copyright 2008, Red Hat, Inc.
2293734Sarybchik   Copyright 2008, Andrew Tridgell.
3293734Sarybchik   Licenced under the same terms as NTP itself.
4293734Sarybchik */
5293734Sarybchik#ifdef HAVE_CONFIG_H
6293734Sarybchik#include <config.h>
7293734Sarybchik#endif
8293734Sarybchik
9293734Sarybchik#ifdef HAVE_NTP_SIGND
10293734Sarybchik
11293734Sarybchik#include "ntpd.h"
12293734Sarybchik#include "ntp_io.h"
13293734Sarybchik#include "ntp_stdlib.h"
14293734Sarybchik#include "ntp_unixtime.h"
15293734Sarybchik#include "ntp_control.h"
16293734Sarybchik#include "ntp_string.h"
17293734Sarybchik
18293734Sarybchik#include <stdio.h>
19293734Sarybchik#include <stddef.h>
20293734Sarybchik#ifdef HAVE_LIBSCF_H
21293734Sarybchik#include <libscf.h>
22293734Sarybchik#include <unistd.h>
23293734Sarybchik#endif /* HAVE_LIBSCF_H */
24293734Sarybchik
25293734Sarybchik#include <sys/un.h>
26293734Sarybchik
27293734Sarybchik/* socket routines by tridge - from junkcode.samba.org */
28293734Sarybchik
29293734Sarybchik/*
30293734Sarybchik  connect to a unix domain socket
31293734Sarybchik*/
32293734Sarybchikstatic int
33293734Sarybchikux_socket_connect(const char *name)
34293734Sarybchik{
35293734Sarybchik	int fd;
36293734Sarybchik	struct sockaddr_un addr;
37293734Sarybchik	if (!name) {
38293734Sarybchik		return -1;
39293734Sarybchik	}
40293748Sarybchik
41293748Sarybchik	ZERO(addr);
42293748Sarybchik	addr.sun_family = AF_UNIX;
43293748Sarybchik	strlcpy(addr.sun_path, name, sizeof(addr.sun_path));
44293748Sarybchik
45293748Sarybchik	fd = socket(AF_UNIX, SOCK_STREAM, 0);
46293748Sarybchik	if (fd == -1) {
47293734Sarybchik		return -1;
48299720Sarybchik	}
49299720Sarybchik
50299720Sarybchik	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
51299720Sarybchik		close(fd);
52299720Sarybchik		return -1;
53299720Sarybchik	}
54299720Sarybchik
55299720Sarybchik	return fd;
56299720Sarybchik}
57299720Sarybchik
58299720Sarybchik
59299720Sarybchik/*
60299720Sarybchik  keep writing until its all sent
61299720Sarybchik*/
62299720Sarybchikstatic int
63299720Sarybchikwrite_all(int fd, const void *buf, size_t len)
64299720Sarybchik{
65299720Sarybchik	size_t total = 0;
66299720Sarybchik	while (len) {
67299720Sarybchik		int n = write(fd, buf, len);
68299720Sarybchik		if (n <= 0) return total;
69299720Sarybchik		buf = n + (char *)buf;
70299720Sarybchik		len -= n;
71299720Sarybchik		total += n;
72299720Sarybchik	}
73299720Sarybchik	return total;
74299720Sarybchik}
75299720Sarybchik
76299720Sarybchik/*
77299720Sarybchik  keep reading until its all read
78299720Sarybchik*/
79299720Sarybchikstatic int
80299720Sarybchikread_all(int fd, void *buf, size_t len)
81299720Sarybchik{
82299720Sarybchik	size_t total = 0;
83299720Sarybchik	while (len) {
84299720Sarybchik		int n = read(fd, buf, len);
85299720Sarybchik		if (n <= 0) return total;
86299720Sarybchik		buf = n + (char *)buf;
87299720Sarybchik		len -= n;
88299720Sarybchik		total += n;
89299720Sarybchik	}
90299720Sarybchik	return total;
91299720Sarybchik}
92299720Sarybchik
93299720Sarybchik/*
94299720Sarybchik  send a packet in length prefix format
95299720Sarybchik*/
96299720Sarybchikstatic int
97299720Sarybchiksend_packet(int fd, const char *buf, uint32_t len)
98299720Sarybchik{
99299720Sarybchik	uint32_t net_len = htonl(len);
100299720Sarybchik	if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
101299720Sarybchik	if (write_all(fd, buf, len) != len) return -1;
102299720Sarybchik	return 0;
103299720Sarybchik}
104299720Sarybchik
105299720Sarybchik/*
106299720Sarybchik  receive a packet in length prefix format
107299720Sarybchik*/
108299720Sarybchikstatic int
109299720Sarybchikrecv_packet(int fd, char **buf, uint32_t *len)
110299720Sarybchik{
111299720Sarybchik	if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
112299720Sarybchik	*len = ntohl(*len);
113299720Sarybchik	(*buf) = emalloc(*len);
114299720Sarybchik	if (read_all(fd, *buf, *len) != *len) {
115299720Sarybchik		free(*buf);
116299720Sarybchik		return -1;
117299720Sarybchik	}
118299720Sarybchik	return 0;
119299720Sarybchik}
120299720Sarybchik
121299720Sarybchikvoid
122299720Sarybchiksend_via_ntp_signd(
123299720Sarybchik	struct recvbuf *rbufp,	/* receive packet pointer */
124299720Sarybchik	int	xmode,
125299720Sarybchik	keyid_t	xkeyid,
126299720Sarybchik	int flags,
127299720Sarybchik	struct pkt  *xpkt
128299720Sarybchik	)
129299720Sarybchik{
130299720Sarybchik
131299720Sarybchik	/* We are here because it was detected that the client
132299720Sarybchik	 * sent an all-zero signature, and we therefore know
133299720Sarybchik	 * it's windows trying to talk to an AD server
134299720Sarybchik	 *
135299720Sarybchik	 * Because we don't want to dive into Samba's secrets
136299720Sarybchik	 * database just to find the long-term kerberos key
137299720Sarybchik	 * that is re-used as the NTP key, we instead hand the
138299720Sarybchik	 * packet over to Samba to sign, and return to us.
139299720Sarybchik	 *
140299720Sarybchik	 * The signing method Samba will use is described by
141299720Sarybchik	 * Microsoft in MS-SNTP, found here:
142299720Sarybchik	 * http://msdn.microsoft.com/en-us/library/cc212930.aspx
143299720Sarybchik	 */
144299720Sarybchik
145299720Sarybchik	int fd, sendlen;
146299720Sarybchik	struct samba_key_in {
147299720Sarybchik		uint32_t version;
148299720Sarybchik		uint32_t op;
149299720Sarybchik		uint32_t packet_id;
150299720Sarybchik		uint32_t key_id_le;
151299720Sarybchik		struct pkt pkt;
152299720Sarybchik	} samba_pkt;
153299720Sarybchik
154299720Sarybchik	struct samba_key_out {
155299720Sarybchik		uint32_t version;
156299720Sarybchik		uint32_t op;
157299720Sarybchik		uint32_t packet_id;
158299720Sarybchik		struct pkt pkt;
159299720Sarybchik	} samba_reply;
160299720Sarybchik
161299720Sarybchik	char full_socket[256];
162299720Sarybchik
163299720Sarybchik	char *reply = NULL;
164299720Sarybchik	uint32_t reply_len;
165299720Sarybchik
166299720Sarybchik	ZERO(samba_pkt);
167299720Sarybchik	samba_pkt.op = 0; /* Sign message */
168299720Sarybchik	/* This will be echoed into the reply - a different
169299720Sarybchik	 * impelementation might want multiple packets
170299720Sarybchik	 * awaiting signing */
171299720Sarybchik
172293887Sarybchik	samba_pkt.packet_id = 1;
173299720Sarybchik
174299720Sarybchik	/* Swap the byte order back - it's actually little
175299720Sarybchik	 * endian on the wire, but it was read above as
176299720Sarybchik	 * network byte order */
177299720Sarybchik	samba_pkt.key_id_le = htonl(xkeyid);
178299720Sarybchik	samba_pkt.pkt = *xpkt;
179299720Sarybchik
180299720Sarybchik	snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
181299720Sarybchik
182299720Sarybchik	fd = ux_socket_connect(full_socket);
183299720Sarybchik	/* Only continue with this if we can talk to Samba */
184299720Sarybchik	if (fd != -1) {
185299720Sarybchik		/* Send old packet to Samba, expect response */
186299720Sarybchik		/* Packet to Samba is quite simple:
187299720Sarybchik		   All values BIG endian except key ID as noted
188299720Sarybchik		   [packet size as BE] - 4 bytes
189299720Sarybchik		   [protocol version (0)] - 4 bytes
190299720Sarybchik		   [packet ID] - 4 bytes
191299720Sarybchik		   [operation (sign message=0)] - 4 bytes
192299720Sarybchik		   [key id] - LITTLE endian (as on wire) - 4 bytes
193299720Sarybchik		   [message to sign] - as marshalled, without signature
194299720Sarybchik		*/
195299720Sarybchik
196299720Sarybchik		if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
197299720Sarybchik			/* Huh?  could not talk to Samba... */
198299720Sarybchik			close(fd);
199299720Sarybchik			return;
200299720Sarybchik		}
201299720Sarybchik
202299720Sarybchik		if (recv_packet(fd, &reply, &reply_len) != 0) {
203299720Sarybchik			if (reply) {
204299720Sarybchik				free(reply);
205299720Sarybchik			}
206299720Sarybchik			close(fd);
207299720Sarybchik			return;
208299720Sarybchik		}
209299720Sarybchik		/* Return packet is also simple:
210299720Sarybchik		   [packet size] - network byte order - 4 bytes
211299720Sarybchik		   [protocol version (0)] network byte order - - 4 bytes
212299720Sarybchik		   [operation (signed success=3, failure=4)] network byte order - - 4 byte
213299720Sarybchik		   (optional) [signed message] - as provided before, with signature appended
214299720Sarybchik		*/
215299720Sarybchik
216299720Sarybchik		if (reply_len <= sizeof(samba_reply)) {
217299720Sarybchik			memcpy(&samba_reply, reply, reply_len);
218299720Sarybchik			if (ntohl(samba_reply.op) == 3 && reply_len >  offsetof(struct samba_key_out, pkt)) {
219299720Sarybchik				sendlen = reply_len - offsetof(struct samba_key_out, pkt);
220299720Sarybchik				xpkt = &samba_reply.pkt;
221299720Sarybchik				sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
222299720Sarybchik#ifdef DEBUG
223299720Sarybchik				if (debug)
224299720Sarybchik					printf(
225299720Sarybchik						"transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
226299720Sarybchik						current_time, ntoa(&rbufp->dstadr->sin),
227299720Sarybchik						ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
228299720Sarybchik#endif
229299720Sarybchik			}
230299720Sarybchik		}
231299720Sarybchik
232299720Sarybchik		if (reply) {
233299720Sarybchik			free(reply);
234299720Sarybchik		}
235299720Sarybchik		close(fd);
236299720Sarybchik
237299720Sarybchik	}
238299720Sarybchik}
239299720Sarybchik#endif
240299720Sarybchik