summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/ntp_signd.c
blob: 857dcbf61082a82b000c2736b8dd0c9a1bdd35b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/* 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;
	}

	ZERO(addr);
	addr.sun_family = AF_UNIX;
	strlcpy(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 + (const 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 = emalloc(*len);
	if (read_all(fd, *buf, *len) != *len) {
		free(*buf);
		*buf = NULL;
		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;
	
	ZERO(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
OpenPOWER on IntegriCloud