/*
 * DF-1400 harness: wi_write_wep LUCENT WEP-key stack overflow.
 *
 * Replicates the EXACT vulnerable logic from:
 *   sys/dev/netif/wi/if_wi.c:1751-1769  (LUCENT branch)
 *      struct wi_key wkey[IEEE80211_WEP_NKID];      // wkey[4] on the stack
 *      ...
 *      keylen = vap->iv_nw_keys[i].wk_keylen;        // up to IEEE80211_KEYBUF_SIZE=16
 *      wkey[i].wi_keylen = htole16(keylen);
 *      memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key, keylen);  // SINK
 *
 * Struct layout verbatim from sys/dev/netif/wi/if_wavelan_ieee.h:290-292:
 *   struct wi_key {
 *       u_int16_t  wi_keylen;
 *       u_int8_t   wi_keydat[14];     // <-- 14-byte destination
 *   };
 * IEEE80211_KEYBUF_SIZE = 16  (sys/netproto/802_11/ieee80211_crypto.h:34)
 * IEEE80211_WEP_NKID    = 4  (sys/netproto/802_11/ieee80211.h:1272)
 *
 * keylen can be 15 or 16 (the WEP cipher accepts >=5 with no upper bound; the
 * ioctl path bounds by IEEE80211_KEYBUF_SIZE=16). memcpy of 16 bytes into the
 * 14-byte wi_keydat overflows by 1-2 bytes. For i==3 (the last wkey slot) the
 * overflow runs off the wkey[4] array and into the surrounding stack frame.
 *
 * The QEMU guest has no Prism/Lucent WiFi hardware, so wi(4) never attaches and
 * wi_write_wep is unreachable live here. This harness proves the primitive
 * deterministically with the exact struct layout.
 *
 * Build:  cc -O2 -o wi_overflow wi_overflow.c
 * Run:    ./wi_overflow
 */
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define IEEE80211_KEYBUF_SIZE 16
#define IEEE80211_WEP_NKID    4

/* if_wavelan_ieee.h:290-292 */
struct wi_key {
	uint16_t wi_keylen;
	uint8_t  wi_keydat[14];
};

int
main(void)
{
	/* Mirror the kernel stack layout: wkey[4] followed by a canary region. */
	struct {
		struct wi_key wkey[IEEE80211_WEP_NKID];
		uint8_t frame_canary[16];
	} s;

	uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
	int keylen = IEEE80211_KEYBUF_SIZE;   /* 16, the max the ioctl path allows */

	memset(&s, 0, sizeof(s));
	memset(s.frame_canary, 0xCC, sizeof(s.frame_canary));  /* canary after wkey[4] */
	memset(wk_key, 0xAB, sizeof(wk_key));                  /* attacker key material */

	printf("DF-1400 wi_write_wep (LUCENT) primitive demonstration\n");
	printf("wi_key.wi_keydat capacity: %zu bytes\n", sizeof(s.wkey[0].wi_keydat));
	printf("keylen (wk_keylen, up to IEEE80211_KEYBUF_SIZE): %d\n", keylen);

	int overflow_slots = 0;
	for (int i = 0; i < IEEE80211_WEP_NKID; i++) {
		/* ---- exact kernel logic (if_wi.c:1766-1769) ---- */
		s.wkey[i].wi_keylen = (uint16_t)keylen;
		memcpy(s.wkey[i].wi_keydat, wk_key, keylen);   /* SINK */
	}

	/* Examine each slot's damage. */
	for (int i = 0; i < IEEE80211_WEP_NKID; i++) {
		/* Bytes beyond wi_keydat[14] for this slot: into the next wi_key's
		 * wi_keylen (or, for i==3, off the array into the frame). */
		unsigned char *after = (unsigned char*)&s.wkey[i].wi_keydat[14];
		int hit = 0;
		for (int b = 0; b < keylen - 14; b++)
			if (after[b] != 0) hit = 1;
		printf("slot %d: wrote %d bytes into 14-byte wi_keydat -> %d byte(s) past end%s\n",
		    i, keylen, keylen - 14,
		    (i == IEEE80211_WEP_NKID - 1) ? " [runs OFF the wkey[4] array into the frame]" : "");
		if (hit) overflow_slots++;
	}

	/* i==3 (last slot): overflow lands in the frame canary. */
	int frame_hit = 0;
	for (size_t b = 0; b < sizeof(s.frame_canary); b++)
		if (s.frame_canary[b] != 0xCC) { frame_hit = 1; break; }

	printf("frame region after wkey[4] corrupted by last slot: %s\n",
	    frame_hit ? "YES" : "no");

	if (keylen > (int)sizeof(s.wkey[0].wi_keydat) && overflow_slots > 0) {
		printf("\nOVERFLOW CONFIRMED: keylen=%d > wi_keydat[14] overflows by %d byte(s) per slot; "
		       "last slot overruns wkey[4] into the stack frame.\n",
		    keylen, keylen - 14);
		printf("On real Prism/Lucent WiFi HW (state transition AUTH/RUN) this is a stack "
		       "overflow in wi_write_wep -> corruption/panic.\n");
		return 0;
	}
	fprintf(stderr, "ERROR: overflow not observed\n");
	return 1;
}
