ntp_signd.c revision 293896
1/* Copyright 2008, Red Hat, Inc. 2 Copyright 2008, Andrew Tridgell. 3 Licenced under the same terms as NTP itself. 4 */ 5#ifdef HAVE_CONFIG_H 6#include <config.h> 7#endif 8 9#ifdef HAVE_NTP_SIGND 10 11#include "ntpd.h" 12#include "ntp_io.h" 13#include "ntp_stdlib.h" 14#include "ntp_unixtime.h" 15#include "ntp_control.h" 16#include "ntp_string.h" 17 18#include <stdio.h> 19#include <stddef.h> 20#ifdef HAVE_LIBSCF_H 21#include <libscf.h> 22#include <unistd.h> 23#endif /* HAVE_LIBSCF_H */ 24 25#include <sys/un.h> 26 27/* socket routines by tridge - from junkcode.samba.org */ 28 29/* 30 connect to a unix domain socket 31*/ 32static int 33ux_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*/ 62static int 63write_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 -= n; 71 total += n; 72 } 73 return total; 74} 75 76/* 77 keep reading until its all read 78*/ 79static int 80read_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 -= n; 88 total += n; 89 } 90 return total; 91} 92 93/* 94 send a packet in length prefix format 95*/ 96static int 97send_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) != len) return -1; 102 return 0; 103} 104 105/* 106 receive a packet in length prefix format 107*/ 108static int 109recv_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) != *len) { 115 free(*buf); 116 *buf = NULL; 117 return -1; 118 } 119 return 0; 120} 121 122void 123send_via_ntp_signd( 124 struct recvbuf *rbufp, /* receive packet pointer */ 125 int xmode, 126 keyid_t xkeyid, 127 int flags, 128 struct pkt *xpkt 129 ) 130{ 131 132 /* We are here because it was detected that the client 133 * sent an all-zero signature, and we therefore know 134 * it's windows trying to talk to an AD server 135 * 136 * Because we don't want to dive into Samba's secrets 137 * database just to find the long-term kerberos key 138 * that is re-used as the NTP key, we instead hand the 139 * packet over to Samba to sign, and return to us. 140 * 141 * The signing method Samba will use is described by 142 * Microsoft in MS-SNTP, found here: 143 * http://msdn.microsoft.com/en-us/library/cc212930.aspx 144 */ 145 146 int fd, sendlen; 147 struct samba_key_in { 148 uint32_t version; 149 uint32_t op; 150 uint32_t packet_id; 151 uint32_t key_id_le; 152 struct pkt pkt; 153 } samba_pkt; 154 155 struct samba_key_out { 156 uint32_t version; 157 uint32_t op; 158 uint32_t packet_id; 159 struct pkt pkt; 160 } samba_reply; 161 162 char full_socket[256]; 163 164 char *reply = NULL; 165 uint32_t reply_len; 166 167 ZERO(samba_pkt); 168 samba_pkt.op = 0; /* Sign message */ 169 /* This will be echoed into the reply - a different 170 * impelementation might want multiple packets 171 * awaiting signing */ 172 173 samba_pkt.packet_id = 1; 174 175 /* Swap the byte order back - it's actually little 176 * endian on the wire, but it was read above as 177 * network byte order */ 178 samba_pkt.key_id_le = htonl(xkeyid); 179 samba_pkt.pkt = *xpkt; 180 181 snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket); 182 183 fd = ux_socket_connect(full_socket); 184 /* Only continue with this if we can talk to Samba */ 185 if (fd != -1) { 186 /* Send old packet to Samba, expect response */ 187 /* Packet to Samba is quite simple: 188 All values BIG endian except key ID as noted 189 [packet size as BE] - 4 bytes 190 [protocol version (0)] - 4 bytes 191 [packet ID] - 4 bytes 192 [operation (sign message=0)] - 4 bytes 193 [key id] - LITTLE endian (as on wire) - 4 bytes 194 [message to sign] - as marshalled, without signature 195 */ 196 197 if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) { 198 /* Huh? could not talk to Samba... */ 199 close(fd); 200 return; 201 } 202 203 if (recv_packet(fd, &reply, &reply_len) != 0) { 204 if (reply) { 205 free(reply); 206 } 207 close(fd); 208 return; 209 } 210 /* Return packet is also simple: 211 [packet size] - network byte order - 4 bytes 212 [protocol version (0)] network byte order - - 4 bytes 213 [operation (signed success=3, failure=4)] network byte order - - 4 byte 214 (optional) [signed message] - as provided before, with signature appended 215 */ 216 217 if (reply_len <= sizeof(samba_reply)) { 218 memcpy(&samba_reply, reply, reply_len); 219 if (ntohl(samba_reply.op) == 3 && reply_len > offsetof(struct samba_key_out, pkt)) { 220 sendlen = reply_len - offsetof(struct samba_key_out, pkt); 221 xpkt = &samba_reply.pkt; 222 sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen); 223#ifdef DEBUG 224 if (debug) 225 printf( 226 "transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n", 227 current_time, ntoa(&rbufp->dstadr->sin), 228 ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen); 229#endif 230 } 231 } 232 233 if (reply) { 234 free(reply); 235 } 236 close(fd); 237 238 } 239} 240#endif 241