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