1/*
2 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "_KPPPAuthenticationHandler.h"
7
8#include <KPPPConfigurePacket.h>
9#include <KPPPDevice.h>
10
11#include <netinet/in.h>
12
13
14static const uint8 kAuthenticationType = 0x3;
15static const char *kAuthenticatorTypeString = "Authenticator";
16
17typedef struct authentication_item {
18	uint8 type;
19	uint8 length;
20	uint16 protocolNumber;
21} _PACKED authentication_item;
22
23
24_KPPPAuthenticationHandler::_KPPPAuthenticationHandler(KPPPInterface& interface)
25	: KPPPOptionHandler("Authentication Handler", kAuthenticationType, interface, NULL),
26	fLocalAuthenticator(NULL),
27	fPeerAuthenticator(NULL),
28	fSuggestedLocalAuthenticator(NULL),
29	fSuggestedPeerAuthenticator(NULL),
30	fPeerAuthenticatorRejected(false)
31{
32}
33
34
35KPPPProtocol*
36_KPPPAuthenticationHandler::NextAuthenticator(const KPPPProtocol *start,
37	ppp_side side) const
38{
39	// find the next authenticator for side, beginning at start
40	KPPPProtocol *current = start ? start->NextProtocol() : Interface().FirstProtocol();
41
42	for (; current; current = current->NextProtocol()) {
43		if (current->Type() && !strcasecmp(current->Type(), kAuthenticatorTypeString)
44				&& current->OptionHandler() && current->Side() == side)
45			return current;
46	}
47
48	return NULL;
49}
50
51
52status_t
53_KPPPAuthenticationHandler::AddToRequest(KPPPConfigurePacket& request)
54{
55	// AddToRequest(): Check if peer must authenticate itself and
56	// add an authentication request if needed. This request is added
57	// by the authenticator's OptionHandler.
58
59	if (fPeerAuthenticator)
60		fPeerAuthenticator->SetEnabled(false);
61	if (fSuggestedPeerAuthenticator)
62		fSuggestedPeerAuthenticator->SetEnabled(false);
63
64	KPPPProtocol *authenticator;
65	if (fPeerAuthenticatorRejected) {
66		if (!fSuggestedPeerAuthenticator) {
67			// This happens when the protocol is rejected, but no alternative
68			// protocol is supplied to us or the suggested protocol is not supported.
69			// We can use this chance to increase fPeerIndex to the next authenticator.
70			authenticator = NextAuthenticator(fPeerAuthenticator, PPP_PEER_SIDE);
71		} else
72			authenticator = fSuggestedPeerAuthenticator;
73
74		fPeerAuthenticatorRejected = false;
75	} else {
76		if (!fPeerAuthenticator) {
77			// there is no authenticator selected, so find one for us
78			authenticator = NextAuthenticator(fPeerAuthenticator, PPP_PEER_SIDE);
79		} else
80			authenticator = fPeerAuthenticator;
81	}
82
83	// check if all authenticators were rejected or if no authentication needed
84	if (!authenticator) {
85		if (fPeerAuthenticator)
86			return B_ERROR;
87				// all authenticators were denied
88		else
89			return B_OK;
90				// no peer authentication needed
91	}
92
93	if (!authenticator || !authenticator->OptionHandler())
94		return B_ERROR;
95
96	fPeerAuthenticator = authenticator;
97		// this could omit some authenticators when we get a suggestion, but that is
98		// no problem because the suggested authenticator will be accepted (hopefully)
99
100	TRACE("KPPPAuthHandler: AddToRequest(%X)\n", authenticator->ProtocolNumber());
101
102	authenticator->SetEnabled(true);
103	return authenticator->OptionHandler()->AddToRequest(request);
104		// let protocol add its request
105}
106
107
108status_t
109_KPPPAuthenticationHandler::ParseNak(const KPPPConfigurePacket& nak)
110{
111	// The authenticator's OptionHandler is not notified.
112
113	authentication_item *item =
114		(authentication_item*) nak.ItemWithType(kAuthenticationType);
115
116	if (!item)
117		return B_OK;
118			// the request was not rejected
119	if (item->length < 4)
120		return B_ERROR;
121
122	if (fSuggestedPeerAuthenticator) {
123		fSuggestedPeerAuthenticator->SetEnabled(false);
124		// if no alternative protocol is supplied we will choose a new one in
125		// AddToRequest()
126		if (ntohs(item->protocolNumber) ==
127				fSuggestedPeerAuthenticator->ProtocolNumber()) {
128			fSuggestedPeerAuthenticator = NULL;
129			return B_OK;
130		}
131	}
132
133	fPeerAuthenticatorRejected = true;
134	KPPPProtocol *authenticator = Interface().ProtocolFor(ntohs(item->protocolNumber));
135	if (authenticator && authenticator->Type()
136			&& !strcasecmp(authenticator->Type(), kAuthenticatorTypeString)
137			&& authenticator->OptionHandler())
138		fSuggestedPeerAuthenticator = authenticator;
139	else
140		fSuggestedPeerAuthenticator = NULL;
141
142	return B_OK;
143}
144
145
146status_t
147_KPPPAuthenticationHandler::ParseReject(const KPPPConfigurePacket& reject)
148{
149	// an authentication request must not be rejected!
150	if (reject.ItemWithType(kAuthenticationType))
151		return B_ERROR;
152
153	return B_OK;
154}
155
156
157status_t
158_KPPPAuthenticationHandler::ParseAck(const KPPPConfigurePacket& ack)
159{
160	authentication_item *item =
161		(authentication_item*) ack.ItemWithType(kAuthenticationType);
162
163	if (!item) {
164		if (fPeerAuthenticator)
165			return B_ERROR;
166				// the ack does not contain our request
167		else
168			return B_OK;
169				// no authentication needed
170	} else if (!fPeerAuthenticator
171			|| ntohs(item->protocolNumber) != fPeerAuthenticator->ProtocolNumber())
172		return B_ERROR;
173			// this item was never requested
174
175	return fPeerAuthenticator->OptionHandler()->ParseAck(ack);
176		// this should enable the authenticator
177}
178
179
180status_t
181_KPPPAuthenticationHandler::ParseRequest(const KPPPConfigurePacket& request,
182	int32 index, KPPPConfigurePacket& nak, KPPPConfigurePacket& reject)
183{
184	if (fLocalAuthenticator)
185		fLocalAuthenticator->SetEnabled(false);
186
187	authentication_item *item = (authentication_item*) request.ItemAt(index);
188	if (!item)
189		return B_OK;
190			// no authentication requested by peer (index > request.CountItems())
191
192	TRACE("KPPPAuthHandler: ParseRequest(%X)\n", ntohs(item->protocolNumber));
193
194	// try to find the requested protocol
195	fLocalAuthenticator = Interface().ProtocolFor(ntohs(item->protocolNumber));
196	if (fLocalAuthenticator && fLocalAuthenticator->Type()
197			&& !strcasecmp(fLocalAuthenticator->Type(), kAuthenticatorTypeString)
198			&& fLocalAuthenticator->OptionHandler())
199		return fLocalAuthenticator->OptionHandler()->ParseRequest(request, index,
200			nak, reject);
201
202	// suggest another authentication protocol
203	KPPPProtocol *nextAuthenticator =
204		NextAuthenticator(fSuggestedLocalAuthenticator, PPP_LOCAL_SIDE);
205
206	if (!nextAuthenticator) {
207		if (!fSuggestedLocalAuthenticator) {
208			// reject the complete authentication option
209			reject.AddItem((ppp_configure_item*) item);
210			return B_OK;
211		} else
212			nextAuthenticator = fSuggestedLocalAuthenticator;
213				// try the old one again as it was not rejected until now
214	}
215
216	fSuggestedLocalAuthenticator = nextAuthenticator;
217	fLocalAuthenticator = NULL;
218		// no authenticator selected
219
220	// nak this authenticator and suggest an alternative
221	authentication_item suggestion;
222	suggestion.type = kAuthenticationType;
223	suggestion.length = 4;
224	suggestion.protocolNumber = htons(nextAuthenticator->ProtocolNumber());
225	return nak.AddItem((ppp_configure_item*) &suggestion) ? B_OK : B_ERROR;
226}
227
228
229status_t
230_KPPPAuthenticationHandler::SendingAck(const KPPPConfigurePacket& ack)
231{
232	// do not insist on authenticating our side of the link ;)
233
234	authentication_item *item =
235		(authentication_item*) ack.ItemWithType(kAuthenticationType);
236
237	if (!item)
238		return B_OK;
239			// no authentication needed
240
241	fSuggestedLocalAuthenticator = NULL;
242
243	if (!fLocalAuthenticator)
244		return B_ERROR;
245			// no authenticator selected (our suggestions must be requested, too)
246
247	if (!fLocalAuthenticator)
248		return B_ERROR;
249
250	fLocalAuthenticator->SetEnabled(true);
251	return fLocalAuthenticator->OptionHandler()->SendingAck(ack);
252		// this should enable the authenticator
253}
254
255
256void
257_KPPPAuthenticationHandler::Reset()
258{
259	if (fLocalAuthenticator) {
260		fLocalAuthenticator->SetEnabled(false);
261		fLocalAuthenticator->OptionHandler()->Reset();
262	}
263	if (fPeerAuthenticator) {
264		fPeerAuthenticator->SetEnabled(false);
265		fPeerAuthenticator->OptionHandler()->Reset();
266	}
267	if (fSuggestedPeerAuthenticator) {
268		fSuggestedPeerAuthenticator->SetEnabled(false);
269		fSuggestedPeerAuthenticator->OptionHandler()->Reset();
270	}
271
272	fLocalAuthenticator = fPeerAuthenticator = fSuggestedLocalAuthenticator =
273		fSuggestedPeerAuthenticator = NULL;
274	fPeerAuthenticatorRejected = false;
275}
276