xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntp_signd.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
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 */
34 static int
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         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 */
64 static int
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 + (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
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
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
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 		return -1;
119 	}
120 	return 0;
121 }
122 
123 void
124 send_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