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