1 /* Copyright Red Hat, Inc.
2    Copyright Andrew Tridgell.
3    Copyright the NTPsec Contributors
4    Licensed under the same terms as NTP itself.
5  */
6 #include "config.h"
7 
8 #ifdef ENABLE_MSSNTP
9 
10 #include "ntpd.h"
11 #include "ntp_io.h"
12 #include "ntp_stdlib.h"
13 
14 #include <string.h>
15 #include <stdio.h>
16 #include <stddef.h>
17 
18 #include <sys/un.h>
19 
20 /* socket routines by tridge - from junkcode.samba.org */
21 /*
22  * Dependency on NTP packet structure removed by ESR.
23  * This code now only knows about the length of an NTP packet header,
24  * not its content. Note that the signing technique never handled anything
25  * but unextended and MACless packet headers, so it can't be used with NTS.
26  */
27 
28 
29 /*
30   connect to a unix domain socket
31 */
32 static int
ux_socket_connect(const char * name)33 ux_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 */
62 static int
write_all(int fd,const void * buf,size_t len)63 write_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 + (const char *)buf;
70 		len -= (unsigned int)n;
71 		total += (unsigned int)n;
72 	}
73 	return total;
74 }
75 
76 /*
77   keep reading until its all read
78 */
79 static int
read_all(int fd,void * buf,size_t len)80 read_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 -= (unsigned int)n;
88 		total += (unsigned int)n;
89 	}
90 	return total;
91 }
92 
93 /*
94   send a packet in length prefix format
95 */
96 static int
send_packet(int fd,const char * buf,uint32_t len)97 send_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) != (int)len) return -1;
102 	return 0;
103 }
104 
105 /*
106   receive a packet in length prefix format
107 */
108 static int
recv_packet(int fd,char ** buf,uint32_t * len)109 recv_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) != (int)*len) {
115 		free(*buf);
116 		return -1;
117 	}
118 	return 0;
119 }
120 
121 void
send_via_ntp_signd(struct recvbuf * rbufp,keyid_t xkeyid,int flags,void * xpkt)122 send_via_ntp_signd(
123 	struct recvbuf *rbufp,	/* receive packet pointer */
124 	keyid_t	xkeyid,
125 	int flags,
126 	void *xpkt
127 	)
128 {
129 	UNUSED_ARG(flags);
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 		char pkt[LEN_PKT_NOMAC];
152 	} samba_pkt;
153 
154 	struct samba_key_out {
155 		uint32_t version;
156 		uint32_t op;
157 		uint32_t packet_id;
158 		char pkt[LEN_PKT_NOMAC];
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 	 * implementation 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 	memcpy(&samba_pkt.pkt, xpkt, sizeof(samba_pkt.pkt));
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 			close(fd);
204 			return;
205 		}
206 		/* Return packet is also simple:
207 		   [packet size] - network byte order - 4 bytes
208 		   [protocol version (0)] network byte order - - 4 bytes
209 		   [operation (signed success=3, failure=4)] network byte order - - 4 byte
210 		   (optional) [signed message] - as provided before, with signature appended
211 		*/
212 
213 		if (reply_len <= sizeof(samba_reply)) {
214 			memcpy(&samba_reply, reply, reply_len);
215 			if (ntohl(samba_reply.op) == 3 && reply_len >  offsetof(struct samba_key_out, pkt)) {
216 				sendlen = reply_len - offsetof(struct samba_key_out, pkt);
217 				xpkt = &samba_reply.pkt;
218 				sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, xpkt, sendlen);
219 				DPRINT(1, ("transmit ntp_signd packet: at %u %s->%s keyid %08x len %d\n",
220 					   current_time, socktoa(&rbufp->dstadr->sin),
221 					   socktoa(&rbufp->recv_srcadr), xkeyid, sendlen));
222 			}
223 		}
224 
225 		if (reply) {
226 			free(reply);
227 		}
228 		close(fd);
229 
230 	}
231 }
232 #endif
233