1/*	$OpenBSD: eap2mschap_local.h,v 1.2 2024/07/16 06:18:20 miod Exp $	*/
2
3/*
4 * Copyright (c) 2024 Internet Initiative Japan Inc.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#define	EAP_CODE_REQUEST	1
20#define	EAP_CODE_RESPONSE	2
21#define	EAP_CODE_SUCCESS	3
22#define	EAP_CODE_FAILURE	4
23
24#define	EAP_TYPE_IDENTITY	1
25#define	EAP_TYPE_NOTIFICATION	2
26#define	EAP_TYPE_NAK		3
27#define	EAP_TYPE_MSCHAPV2	0x1a	/* [MS-CHAP] MS-EAP-Authentication */
28
29#define CHAP_CHALLENGE		1
30#define CHAP_RESPONSE		2
31#define CHAP_SUCCESS		3
32#define CHAP_FAILURE		4
33
34/* From [MS-CHAP] */
35enum eap_chap_status {
36	EAP_CHAP_NONE,
37	EAP_CHAP_CHALLENGE_SENT,
38	EAP_CHAP_SUCCESS_REQUEST_SENT,
39	EAP_CHAP_FAILURE_REQUEST_SENT,
40	EAP_CHAP_CHANGE_PASSWORD_SENT,
41	EAP_CHAP_FAILED,
42	EAP_CHAP_SUCCESS
43};
44
45struct eap {
46	uint8_t		code;
47	uint8_t		id;
48	uint16_t	length;
49	uint8_t		value[0];
50} __packed;
51
52struct chap {
53	uint8_t		code;
54	uint8_t		id;
55	uint16_t	length;
56	int8_t		value[0];
57} __packed;
58
59struct eap_chap {
60	struct eap	eap;
61	uint8_t		eap_type;
62	struct chap	chap;
63};
64
65struct eap_mschap_challenge {
66	struct eap	eap;
67	uint8_t		eap_type;
68	struct chap	chap;
69	uint8_t		challsiz;
70	uint8_t		chall[16];
71	char		chap_name[0];
72} __packed;
73#if defined(__STDC_VERSION__) && __STDC_VERSION >= 201112L
74static_assert(sizeof(struct eap_mschap_challenge) == 26, "");
75static_assert(offsetof(struct eap_mschap_challenge, chap) == 5, "");
76static_assert(offsetof(struct eap_mschap_challenge, chall) == 10, "");
77#endif
78
79struct eap_mschap_response {
80	struct eap	eap;
81	uint8_t		eap_type;
82	struct chap	chap;
83	uint8_t		challsiz;
84	uint8_t		peerchall[16];
85	uint8_t		reserved[8];
86	uint8_t		ntresponse[24];
87	uint8_t		flags;
88	uint8_t		chap_name[0];
89} __packed;
90#if defined(__STDC_VERSION__) && __STDC_VERSION >= 201112L
91static_assert(sizeof(struct eap_mschap_response) == 59, "");
92static_assert(offsetof(struct eap_mschap_response, chap) == 5, "");
93static_assert(offsetof(struct eap_mschap_response, peerchall) == 10, "");
94#endif
95
96struct radius_ms_chap2_response {
97	uint8_t		ident;
98	uint8_t		flags;
99	uint8_t		peerchall[16];
100	uint8_t		reserved[8];
101	uint8_t		ntresponse[24];
102} __packed;
103
104
105struct eap2mschap;
106
107struct access_req {
108	struct eap2mschap	*eap2mschap;
109	char			*username;
110	u_int			 q_id;
111	TAILQ_ENTRY(access_req)	 next;
112	RB_ENTRY(access_req)	 tree;
113	/* for EAP */
114	enum eap_chap_status	 eap_chap_status;
115	char			 state[16];
116	unsigned char		 chap_id;
117	unsigned char		 eap_id;
118	time_t			 eap_time;
119	char			 chall[16];
120	RADIUS_PACKET		*pkt;
121
122};
123TAILQ_HEAD(access_reqq, access_req);
124RB_HEAD(access_reqt, access_req);
125
126#define CHAP_NAME_MAX			40
127
128struct eap2mschap {
129	struct module_base	*base;
130	char			*secret;
131	char			 chap_name[CHAP_NAME_MAX + 1];
132	struct access_reqq	 reqq;
133	struct access_reqt	 eapt;
134	struct event		 ev_eapt;
135};
136
137/* Attributes copied from CHAP Access-Accept to EAP Access-Access-Accept */
138struct preserve_attrs {
139	uint8_t		type;
140	uint32_t	vendor;
141} preserve_attrs[] = {
142	{ RADIUS_TYPE_FRAMED_PROTOCOL,		0},
143	{ RADIUS_TYPE_FRAMED_IP_ADDRESS,	0},
144	{ RADIUS_TYPE_FRAMED_IP_NETMASK,	0},
145	{ RADIUS_TYPE_FRAMED_IPV6_ADDRESS,	0},
146	{ RADIUS_TYPE_DNS_SERVER_IPV6_ADDRESS,	0},
147	{ RADIUS_TYPE_FRAMED_ROUTING,		0},
148	{ RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER,	RADIUS_VENDOR_MICROSOFT },
149	{ RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER,	RADIUS_VENDOR_MICROSOFT },
150	{ RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER,	RADIUS_VENDOR_MICROSOFT },
151	{ RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER,RADIUS_VENDOR_MICROSOFT },
152	{ RADIUS_VTYPE_MPPE_SEND_KEY,		RADIUS_VENDOR_MICROSOFT },
153	{ RADIUS_VTYPE_MPPE_RECV_KEY, 		RADIUS_VENDOR_MICROSOFT }
154};
155
156#ifndef EAP2MSCHAP_DEBUG
157#define	EAP2MSCHAP_DBG(...)
158#define	EAP2MSCHAP_ASSERT(_cond)
159#else
160#define	EAP2MSCHAP_DBG(...)	logit(LOG_DEBUG, __VA_ARGS__)
161#define	EAP2MSCHAP_ASSERT(_cond)				\
162	do {							\
163		if (!(_cond)) {					\
164			log_warnx(				\
165			    "ASSERT(%s) failed in %s() at %s:%d",\
166			    #_cond, __func__, __FILE__, __LINE__);\
167			abort();				\
168		}						\
169	} while (0/* CONSTCOND */);
170#endif
171#ifndef nitems
172#define nitems(_x)    (sizeof((_x)) / sizeof((_x)[0]))
173#endif
174
175static void	 eap2mschap_init(struct eap2mschap *);
176static void	 eap2mschap_start(void *);
177static void	 eap2mschap_config_set(void *, const char *, int,
178		    char * const *);
179static void	 eap2mschap_stop(void *);
180static void	 eap2mschap_access_request(void *, u_int, const u_char *,
181		    size_t);
182static void	 eap2mschap_next_response(void *, u_int, const u_char *,
183		    size_t);
184
185static void	 eap2mschap_on_eapt (int, short, void *);
186static void	 eap2mschap_reset_eaptimer (struct eap2mschap *);
187
188static struct access_req
189		*access_request_new(struct eap2mschap *, u_int);
190static void	 access_request_free(struct access_req *);
191static int	 access_request_compar(struct access_req *,
192		    struct access_req *);
193
194
195static struct access_req
196		*eap_recv(struct eap2mschap *, u_int, RADIUS_PACKET *);
197static struct access_req
198		*eap_recv_mschap(struct eap2mschap *, struct access_req *,
199		    RADIUS_PACKET *, struct eap_chap *);
200static void	 eap_resp_mschap(struct eap2mschap *, struct access_req *,
201		    RADIUS_PACKET *);
202static void	 eap_send_reject(struct access_req *, RADIUS_PACKET *, u_int);
203static const char
204		*eap_chap_status_string(enum eap_chap_status);
205static const char
206		*hex_string(const char *, size_t, char *, size_t);
207static time_t	 monotime(void);
208
209RB_PROTOTYPE_STATIC(access_reqt, access_req, tree, access_request_compar);
210