xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntp_signd.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
1 /*	$NetBSD: ntp_signd.c,v 1.5 2020/05/25 20:47:25 christos 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 */
34 static int
ux_socket_connect(const char * name)35 ux_socket_connect(const char *name)
36 {
37 	int fd;
38 	struct sockaddr_un addr;
39 	if (!name) {
40 		return -1;
41 	}
42 
43 	ZERO(addr);
44 	addr.sun_family = AF_UNIX;
45 	strlcpy(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 */
64 static int
write_all(int fd,const void * buf,size_t len)65 write_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 + (const char *)buf;
72 		len -= n;
73 		total += n;
74 	}
75 	return total;
76 }
77 
78 /*
79   keep reading until its all read
80 */
81 static int
read_all(int fd,void * buf,size_t len)82 read_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 */
98 static int
send_packet(int fd,const char * buf,uint32_t len)99 send_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 */
110 static int
recv_packet(int fd,char ** buf,uint32_t * len)111 recv_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 		*buf = NULL;
119 		return -1;
120 	}
121 	return 0;
122 }
123 
124 void
send_via_ntp_signd(struct recvbuf * rbufp,int xmode,keyid_t xkeyid,int flags,struct pkt * xpkt)125 send_via_ntp_signd(
126 	struct recvbuf *rbufp,	/* receive packet pointer */
127 	int	xmode,
128 	keyid_t	xkeyid,
129 	int flags,
130 	struct pkt  *xpkt
131 	)
132 {
133 
134 	/* We are here because it was detected that the client
135 	 * sent an all-zero signature, and we therefore know
136 	 * it's windows trying to talk to an AD server
137 	 *
138 	 * Because we don't want to dive into Samba's secrets
139 	 * database just to find the long-term kerberos key
140 	 * that is re-used as the NTP key, we instead hand the
141 	 * packet over to Samba to sign, and return to us.
142 	 *
143 	 * The signing method Samba will use is described by
144 	 * Microsoft in MS-SNTP, found here:
145 	 * http://msdn.microsoft.com/en-us/library/cc212930.aspx
146 	 */
147 
148 	int fd, sendlen;
149 	struct samba_key_in {
150 		uint32_t version;
151 		uint32_t op;
152 		uint32_t packet_id;
153 		uint32_t key_id_le;
154 		struct pkt pkt;
155 	} samba_pkt;
156 
157 	struct samba_key_out {
158 		uint32_t version;
159 		uint32_t op;
160 		uint32_t packet_id;
161 		struct pkt pkt;
162 	} samba_reply;
163 
164 	char full_socket[256];
165 
166 	char *reply = NULL;
167 	uint32_t reply_len;
168 
169 	ZERO(samba_pkt);
170 	samba_pkt.op = 0; /* Sign message */
171 	/* This will be echoed into the reply - a different
172 	 * impelementation might want multiple packets
173 	 * awaiting signing */
174 
175 	samba_pkt.packet_id = 1;
176 
177 	/* Swap the byte order back - it's actually little
178 	 * endian on the wire, but it was read above as
179 	 * network byte order */
180 	samba_pkt.key_id_le = htonl(xkeyid);
181 	samba_pkt.pkt = *xpkt;
182 
183 	snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
184 
185 	fd = ux_socket_connect(full_socket);
186 	/* Only continue with this if we can talk to Samba */
187 	if (fd != -1) {
188 		/* Send old packet to Samba, expect response */
189 		/* Packet to Samba is quite simple:
190 		   All values BIG endian except key ID as noted
191 		   [packet size as BE] - 4 bytes
192 		   [protocol version (0)] - 4 bytes
193 		   [packet ID] - 4 bytes
194 		   [operation (sign message=0)] - 4 bytes
195 		   [key id] - LITTLE endian (as on wire) - 4 bytes
196 		   [message to sign] - as marshalled, without signature
197 		*/
198 
199 		if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
200 			/* Huh?  could not talk to Samba... */
201 			close(fd);
202 			return;
203 		}
204 
205 		if (recv_packet(fd, &reply, &reply_len) != 0) {
206 			if (reply) {
207 				free(reply);
208 			}
209 			close(fd);
210 			return;
211 		}
212 		/* Return packet is also simple:
213 		   [packet size] - network byte order - 4 bytes
214 		   [protocol version (0)] network byte order - - 4 bytes
215 		   [operation (signed success=3, failure=4)] network byte order - - 4 byte
216 		   (optional) [signed message] - as provided before, with signature appended
217 		*/
218 
219 		if (reply_len <= sizeof(samba_reply)) {
220 			memcpy(&samba_reply, reply, reply_len);
221 			if (ntohl(samba_reply.op) == 3 && reply_len >  offsetof(struct samba_key_out, pkt)) {
222 				sendlen = reply_len - offsetof(struct samba_key_out, pkt);
223 				xpkt = &samba_reply.pkt;
224 				sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
225 #ifdef DEBUG
226 				if (debug)
227 					printf(
228 						"transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
229 						current_time, ntoa(&rbufp->dstadr->sin),
230 						ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
231 #endif
232 			}
233 		}
234 
235 		if (reply) {
236 			free(reply);
237 		}
238 		close(fd);
239 
240 	}
241 }
242 #endif
243