1 | /*
|
---|
2 | * chap-new.c - New CHAP implementation.
|
---|
3 | *
|
---|
4 | * Copyright (c) 2003 Paul Mackerras. All rights reserved.
|
---|
5 | *
|
---|
6 | * Redistribution and use in source and binary forms, with or without
|
---|
7 | * modification, are permitted provided that the following conditions
|
---|
8 | * are met:
|
---|
9 | *
|
---|
10 | * 1. Redistributions of source code must retain the above copyright
|
---|
11 | * notice, this list of conditions and the following disclaimer.
|
---|
12 | *
|
---|
13 | * 2. The name(s) of the authors of this software must not be used to
|
---|
14 | * endorse or promote products derived from this software without
|
---|
15 | * prior written permission.
|
---|
16 | *
|
---|
17 | * 3. Redistributions of any form whatsoever must retain the following
|
---|
18 | * acknowledgment:
|
---|
19 | * "This product includes software developed by Paul Mackerras
|
---|
20 | * <paulus@samba.org>".
|
---|
21 | *
|
---|
22 | * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
|
---|
23 | * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
---|
24 | * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
---|
25 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
---|
26 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
---|
27 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
---|
28 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
---|
29 | */
|
---|
30 |
|
---|
31 | #include "netif/ppp/ppp_opts.h"
|
---|
32 | #if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
|
---|
33 |
|
---|
34 | #if 0 /* UNUSED */
|
---|
35 | #include <stdlib.h>
|
---|
36 | #include <string.h>
|
---|
37 | #endif /* UNUSED */
|
---|
38 |
|
---|
39 | #include "netif/ppp/ppp_impl.h"
|
---|
40 |
|
---|
41 | #if 0 /* UNUSED */
|
---|
42 | #include "session.h"
|
---|
43 | #endif /* UNUSED */
|
---|
44 |
|
---|
45 | #include "netif/ppp/chap-new.h"
|
---|
46 | #include "netif/ppp/chap-md5.h"
|
---|
47 | #if MSCHAP_SUPPORT
|
---|
48 | #include "netif/ppp/chap_ms.h"
|
---|
49 | #endif
|
---|
50 | #include "netif/ppp/magic.h"
|
---|
51 |
|
---|
52 | #if 0 /* UNUSED */
|
---|
53 | /* Hook for a plugin to validate CHAP challenge */
|
---|
54 | int (*chap_verify_hook)(const char *name, const char *ourname, int id,
|
---|
55 | const struct chap_digest_type *digest,
|
---|
56 | const unsigned char *challenge, const unsigned char *response,
|
---|
57 | char *message, int message_space) = NULL;
|
---|
58 | #endif /* UNUSED */
|
---|
59 |
|
---|
60 | #if PPP_OPTIONS
|
---|
61 | /*
|
---|
62 | * Command-line options.
|
---|
63 | */
|
---|
64 | static option_t chap_option_list[] = {
|
---|
65 | { "chap-restart", o_int, &chap_timeout_time,
|
---|
66 | "Set timeout for CHAP", OPT_PRIO },
|
---|
67 | { "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits,
|
---|
68 | "Set max #xmits for challenge", OPT_PRIO },
|
---|
69 | { "chap-interval", o_int, &pcb->settings.chap_rechallenge_time,
|
---|
70 | "Set interval for rechallenge", OPT_PRIO },
|
---|
71 | { NULL }
|
---|
72 | };
|
---|
73 | #endif /* PPP_OPTIONS */
|
---|
74 |
|
---|
75 |
|
---|
76 | /* Values for flags in chap_client_state and chap_server_state */
|
---|
77 | #define LOWERUP 1
|
---|
78 | #define AUTH_STARTED 2
|
---|
79 | #define AUTH_DONE 4
|
---|
80 | #define AUTH_FAILED 8
|
---|
81 | #define TIMEOUT_PENDING 0x10
|
---|
82 | #define CHALLENGE_VALID 0x20
|
---|
83 |
|
---|
84 | /*
|
---|
85 | * Prototypes.
|
---|
86 | */
|
---|
87 | static void chap_init(ppp_pcb *pcb);
|
---|
88 | static void chap_lowerup(ppp_pcb *pcb);
|
---|
89 | static void chap_lowerdown(ppp_pcb *pcb);
|
---|
90 | #if PPP_SERVER
|
---|
91 | static void chap_timeout(void *arg);
|
---|
92 | static void chap_generate_challenge(ppp_pcb *pcb);
|
---|
93 | static void chap_handle_response(ppp_pcb *pcb, int code,
|
---|
94 | unsigned char *pkt, int len);
|
---|
95 | static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
|
---|
96 | const struct chap_digest_type *digest,
|
---|
97 | const unsigned char *challenge, const unsigned char *response,
|
---|
98 | char *message, int message_space);
|
---|
99 | #endif /* PPP_SERVER */
|
---|
100 | static void chap_respond(ppp_pcb *pcb, int id,
|
---|
101 | unsigned char *pkt, int len);
|
---|
102 | static void chap_handle_status(ppp_pcb *pcb, int code, int id,
|
---|
103 | unsigned char *pkt, int len);
|
---|
104 | static void chap_protrej(ppp_pcb *pcb);
|
---|
105 | static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen);
|
---|
106 | #if PRINTPKT_SUPPORT
|
---|
107 | static int chap_print_pkt(const unsigned char *p, int plen,
|
---|
108 | void (*printer) (void *, const char *, ...), void *arg);
|
---|
109 | #endif /* PRINTPKT_SUPPORT */
|
---|
110 |
|
---|
111 | /* List of digest types that we know about */
|
---|
112 | static const struct chap_digest_type* const chap_digests[] = {
|
---|
113 | &md5_digest,
|
---|
114 | #if MSCHAP_SUPPORT
|
---|
115 | &chapms_digest,
|
---|
116 | &chapms2_digest,
|
---|
117 | #endif /* MSCHAP_SUPPORT */
|
---|
118 | NULL
|
---|
119 | };
|
---|
120 |
|
---|
121 | /*
|
---|
122 | * chap_init - reset to initial state.
|
---|
123 | */
|
---|
124 | static void chap_init(ppp_pcb *pcb) {
|
---|
125 | LWIP_UNUSED_ARG(pcb);
|
---|
126 |
|
---|
127 | #if 0 /* Not necessary, everything is cleared in ppp_new() */
|
---|
128 | memset(&pcb->chap_client, 0, sizeof(chap_client_state));
|
---|
129 | #if PPP_SERVER
|
---|
130 | memset(&pcb->chap_server, 0, sizeof(chap_server_state));
|
---|
131 | #endif /* PPP_SERVER */
|
---|
132 | #endif /* 0 */
|
---|
133 | }
|
---|
134 |
|
---|
135 | /*
|
---|
136 | * chap_lowerup - we can start doing stuff now.
|
---|
137 | */
|
---|
138 | static void chap_lowerup(ppp_pcb *pcb) {
|
---|
139 |
|
---|
140 | pcb->chap_client.flags |= LOWERUP;
|
---|
141 | #if PPP_SERVER
|
---|
142 | pcb->chap_server.flags |= LOWERUP;
|
---|
143 | if (pcb->chap_server.flags & AUTH_STARTED)
|
---|
144 | chap_timeout(pcb);
|
---|
145 | #endif /* PPP_SERVER */
|
---|
146 | }
|
---|
147 |
|
---|
148 | static void chap_lowerdown(ppp_pcb *pcb) {
|
---|
149 |
|
---|
150 | pcb->chap_client.flags = 0;
|
---|
151 | #if PPP_SERVER
|
---|
152 | if (pcb->chap_server.flags & TIMEOUT_PENDING)
|
---|
153 | UNTIMEOUT(chap_timeout, pcb);
|
---|
154 | pcb->chap_server.flags = 0;
|
---|
155 | #endif /* PPP_SERVER */
|
---|
156 | }
|
---|
157 |
|
---|
158 | #if PPP_SERVER
|
---|
159 | /*
|
---|
160 | * chap_auth_peer - Start authenticating the peer.
|
---|
161 | * If the lower layer is already up, we start sending challenges,
|
---|
162 | * otherwise we wait for the lower layer to come up.
|
---|
163 | */
|
---|
164 | void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
|
---|
165 | const struct chap_digest_type *dp;
|
---|
166 | int i;
|
---|
167 |
|
---|
168 | if (pcb->chap_server.flags & AUTH_STARTED) {
|
---|
169 | ppp_error("CHAP: peer authentication already started!");
|
---|
170 | return;
|
---|
171 | }
|
---|
172 | for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
|
---|
173 | if (dp->code == digest_code)
|
---|
174 | break;
|
---|
175 | if (dp == NULL)
|
---|
176 | ppp_fatal("CHAP digest 0x%x requested but not available",
|
---|
177 | digest_code);
|
---|
178 |
|
---|
179 | pcb->chap_server.digest = dp;
|
---|
180 | pcb->chap_server.name = our_name;
|
---|
181 | /* Start with a random ID value */
|
---|
182 | pcb->chap_server.id = magic();
|
---|
183 | pcb->chap_server.flags |= AUTH_STARTED;
|
---|
184 | if (pcb->chap_server.flags & LOWERUP)
|
---|
185 | chap_timeout(pcb);
|
---|
186 | }
|
---|
187 | #endif /* PPP_SERVER */
|
---|
188 |
|
---|
189 | /*
|
---|
190 | * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
|
---|
191 | * There isn't much to do until we receive a challenge.
|
---|
192 | */
|
---|
193 | void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
|
---|
194 | const struct chap_digest_type *dp;
|
---|
195 | int i;
|
---|
196 |
|
---|
197 | if(NULL == our_name)
|
---|
198 | return;
|
---|
199 |
|
---|
200 | if (pcb->chap_client.flags & AUTH_STARTED) {
|
---|
201 | ppp_error("CHAP: authentication with peer already started!");
|
---|
202 | return;
|
---|
203 | }
|
---|
204 | for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
|
---|
205 | if (dp->code == digest_code)
|
---|
206 | break;
|
---|
207 |
|
---|
208 | if (dp == NULL)
|
---|
209 | ppp_fatal("CHAP digest 0x%x requested but not available",
|
---|
210 | digest_code);
|
---|
211 |
|
---|
212 | pcb->chap_client.digest = dp;
|
---|
213 | pcb->chap_client.name = our_name;
|
---|
214 | pcb->chap_client.flags |= AUTH_STARTED;
|
---|
215 | }
|
---|
216 |
|
---|
217 | #if PPP_SERVER
|
---|
218 | /*
|
---|
219 | * chap_timeout - It's time to send another challenge to the peer.
|
---|
220 | * This could be either a retransmission of a previous challenge,
|
---|
221 | * or a new challenge to start re-authentication.
|
---|
222 | */
|
---|
223 | static void chap_timeout(void *arg) {
|
---|
224 | ppp_pcb *pcb = (ppp_pcb*)arg;
|
---|
225 | struct pbuf *p;
|
---|
226 |
|
---|
227 | pcb->chap_server.flags &= ~TIMEOUT_PENDING;
|
---|
228 | if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) {
|
---|
229 | pcb->chap_server.challenge_xmits = 0;
|
---|
230 | chap_generate_challenge(pcb);
|
---|
231 | pcb->chap_server.flags |= CHALLENGE_VALID;
|
---|
232 | } else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) {
|
---|
233 | pcb->chap_server.flags &= ~CHALLENGE_VALID;
|
---|
234 | pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED;
|
---|
235 | auth_peer_fail(pcb, PPP_CHAP);
|
---|
236 | return;
|
---|
237 | }
|
---|
238 |
|
---|
239 | p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE);
|
---|
240 | if(NULL == p)
|
---|
241 | return;
|
---|
242 | if(p->tot_len != p->len) {
|
---|
243 | pbuf_free(p);
|
---|
244 | return;
|
---|
245 | }
|
---|
246 | MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen);
|
---|
247 | ppp_write(pcb, p);
|
---|
248 | ++pcb->chap_server.challenge_xmits;
|
---|
249 | pcb->chap_server.flags |= TIMEOUT_PENDING;
|
---|
250 | TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time);
|
---|
251 | }
|
---|
252 |
|
---|
253 | /*
|
---|
254 | * chap_generate_challenge - generate a challenge string and format
|
---|
255 | * the challenge packet in pcb->chap_server.challenge_pkt.
|
---|
256 | */
|
---|
257 | static void chap_generate_challenge(ppp_pcb *pcb) {
|
---|
258 | int clen = 1, nlen, len;
|
---|
259 | unsigned char *p;
|
---|
260 |
|
---|
261 | p = pcb->chap_server.challenge;
|
---|
262 | MAKEHEADER(p, PPP_CHAP);
|
---|
263 | p += CHAP_HDRLEN;
|
---|
264 | pcb->chap_server.digest->generate_challenge(pcb, p);
|
---|
265 | clen = *p;
|
---|
266 | nlen = strlen(pcb->chap_server.name);
|
---|
267 | memcpy(p + 1 + clen, pcb->chap_server.name, nlen);
|
---|
268 |
|
---|
269 | len = CHAP_HDRLEN + 1 + clen + nlen;
|
---|
270 | pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len;
|
---|
271 |
|
---|
272 | p = pcb->chap_server.challenge + PPP_HDRLEN;
|
---|
273 | p[0] = CHAP_CHALLENGE;
|
---|
274 | p[1] = ++pcb->chap_server.id;
|
---|
275 | p[2] = len >> 8;
|
---|
276 | p[3] = len;
|
---|
277 | }
|
---|
278 |
|
---|
279 | /*
|
---|
280 | * chap_handle_response - check the response to our challenge.
|
---|
281 | */
|
---|
282 | static void chap_handle_response(ppp_pcb *pcb, int id,
|
---|
283 | unsigned char *pkt, int len) {
|
---|
284 | int response_len, ok, mlen;
|
---|
285 | const unsigned char *response;
|
---|
286 | unsigned char *outp;
|
---|
287 | struct pbuf *p;
|
---|
288 | const char *name = NULL; /* initialized to shut gcc up */
|
---|
289 | #if 0 /* UNUSED */
|
---|
290 | int (*verifier)(const char *, const char *, int, const struct chap_digest_type *,
|
---|
291 | const unsigned char *, const unsigned char *, char *, int);
|
---|
292 | #endif /* UNUSED */
|
---|
293 | char rname[MAXNAMELEN+1];
|
---|
294 | char message[256];
|
---|
295 |
|
---|
296 | if ((pcb->chap_server.flags & LOWERUP) == 0)
|
---|
297 | return;
|
---|
298 | if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2)
|
---|
299 | return;
|
---|
300 | if (pcb->chap_server.flags & CHALLENGE_VALID) {
|
---|
301 | response = pkt;
|
---|
302 | GETCHAR(response_len, pkt);
|
---|
303 | len -= response_len + 1; /* length of name */
|
---|
304 | name = (char *)pkt + response_len;
|
---|
305 | if (len < 0)
|
---|
306 | return;
|
---|
307 |
|
---|
308 | if (pcb->chap_server.flags & TIMEOUT_PENDING) {
|
---|
309 | pcb->chap_server.flags &= ~TIMEOUT_PENDING;
|
---|
310 | UNTIMEOUT(chap_timeout, pcb);
|
---|
311 | }
|
---|
312 | #if PPP_REMOTENAME
|
---|
313 | if (pcb->settings.explicit_remote) {
|
---|
314 | name = pcb->remote_name;
|
---|
315 | } else
|
---|
316 | #endif /* PPP_REMOTENAME */
|
---|
317 | {
|
---|
318 | /* Null terminate and clean remote name. */
|
---|
319 | ppp_slprintf(rname, sizeof(rname), "%.*v", len, name);
|
---|
320 | name = rname;
|
---|
321 | }
|
---|
322 |
|
---|
323 | #if 0 /* UNUSED */
|
---|
324 | if (chap_verify_hook)
|
---|
325 | verifier = chap_verify_hook;
|
---|
326 | else
|
---|
327 | verifier = chap_verify_response;
|
---|
328 | ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest,
|
---|
329 | pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
|
---|
330 | response, pcb->chap_server.message, sizeof(pcb->chap_server.message));
|
---|
331 | #endif /* UNUSED */
|
---|
332 | ok = chap_verify_response(pcb, name, pcb->chap_server.name, id, pcb->chap_server.digest,
|
---|
333 | pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
|
---|
334 | response, message, sizeof(message));
|
---|
335 | #if 0 /* UNUSED */
|
---|
336 | if (!ok || !auth_number()) {
|
---|
337 | #endif /* UNUSED */
|
---|
338 | if (!ok) {
|
---|
339 | pcb->chap_server.flags |= AUTH_FAILED;
|
---|
340 | ppp_warn("Peer %q failed CHAP authentication", name);
|
---|
341 | }
|
---|
342 | } else if ((pcb->chap_server.flags & AUTH_DONE) == 0)
|
---|
343 | return;
|
---|
344 |
|
---|
345 | /* send the response */
|
---|
346 | mlen = strlen(message);
|
---|
347 | len = CHAP_HDRLEN + mlen;
|
---|
348 | p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE);
|
---|
349 | if(NULL == p)
|
---|
350 | return;
|
---|
351 | if(p->tot_len != p->len) {
|
---|
352 | pbuf_free(p);
|
---|
353 | return;
|
---|
354 | }
|
---|
355 |
|
---|
356 | outp = (unsigned char *)p->payload;
|
---|
357 | MAKEHEADER(outp, PPP_CHAP);
|
---|
358 |
|
---|
359 | outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
|
---|
360 | outp[1] = id;
|
---|
361 | outp[2] = len >> 8;
|
---|
362 | outp[3] = len;
|
---|
363 | if (mlen > 0)
|
---|
364 | memcpy(outp + CHAP_HDRLEN, message, mlen);
|
---|
365 | ppp_write(pcb, p);
|
---|
366 |
|
---|
367 | if (pcb->chap_server.flags & CHALLENGE_VALID) {
|
---|
368 | pcb->chap_server.flags &= ~CHALLENGE_VALID;
|
---|
369 | if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) {
|
---|
370 |
|
---|
371 | #if 0 /* UNUSED */
|
---|
372 | /*
|
---|
373 | * Auth is OK, so now we need to check session restrictions
|
---|
374 | * to ensure everything is OK, but only if we used a
|
---|
375 | * plugin, and only if we're configured to check. This
|
---|
376 | * allows us to do PAM checks on PPP servers that
|
---|
377 | * authenticate against ActiveDirectory, and use AD for
|
---|
378 | * account info (like when using Winbind integrated with
|
---|
379 | * PAM).
|
---|
380 | */
|
---|
381 | if (session_mgmt &&
|
---|
382 | session_check(name, NULL, devnam, NULL) == 0) {
|
---|
383 | pcb->chap_server.flags |= AUTH_FAILED;
|
---|
384 | ppp_warn("Peer %q failed CHAP Session verification", name);
|
---|
385 | }
|
---|
386 | #endif /* UNUSED */
|
---|
387 |
|
---|
388 | }
|
---|
389 | if (pcb->chap_server.flags & AUTH_FAILED) {
|
---|
390 | auth_peer_fail(pcb, PPP_CHAP);
|
---|
391 | } else {
|
---|
392 | if ((pcb->chap_server.flags & AUTH_DONE) == 0)
|
---|
393 | auth_peer_success(pcb, PPP_CHAP,
|
---|
394 | pcb->chap_server.digest->code,
|
---|
395 | name, strlen(name));
|
---|
396 | if (pcb->settings.chap_rechallenge_time) {
|
---|
397 | pcb->chap_server.flags |= TIMEOUT_PENDING;
|
---|
398 | TIMEOUT(chap_timeout, pcb,
|
---|
399 | pcb->settings.chap_rechallenge_time);
|
---|
400 | }
|
---|
401 | }
|
---|
402 | pcb->chap_server.flags |= AUTH_DONE;
|
---|
403 | }
|
---|
404 | }
|
---|
405 |
|
---|
406 | /*
|
---|
407 | * chap_verify_response - check whether the peer's response matches
|
---|
408 | * what we think it should be. Returns 1 if it does (authentication
|
---|
409 | * succeeded), or 0 if it doesn't.
|
---|
410 | */
|
---|
411 | static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
|
---|
412 | const struct chap_digest_type *digest,
|
---|
413 | const unsigned char *challenge, const unsigned char *response,
|
---|
414 | char *message, int message_space) {
|
---|
415 | int ok;
|
---|
416 | unsigned char secret[MAXSECRETLEN];
|
---|
417 | int secret_len;
|
---|
418 |
|
---|
419 | /* Get the secret that the peer is supposed to know */
|
---|
420 | if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) {
|
---|
421 | ppp_error("No CHAP secret found for authenticating %q", name);
|
---|
422 | return 0;
|
---|
423 | }
|
---|
424 | ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge,
|
---|
425 | response, message, message_space);
|
---|
426 | memset(secret, 0, sizeof(secret));
|
---|
427 |
|
---|
428 | return ok;
|
---|
429 | }
|
---|
430 | #endif /* PPP_SERVER */
|
---|
431 |
|
---|
432 | /*
|
---|
433 | * chap_respond - Generate and send a response to a challenge.
|
---|
434 | */
|
---|
435 | static void chap_respond(ppp_pcb *pcb, int id,
|
---|
436 | unsigned char *pkt, int len) {
|
---|
437 | int clen, nlen;
|
---|
438 | int secret_len;
|
---|
439 | struct pbuf *p;
|
---|
440 | u_char *outp;
|
---|
441 | char rname[MAXNAMELEN+1];
|
---|
442 | char secret[MAXSECRETLEN+1];
|
---|
443 |
|
---|
444 | p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE);
|
---|
445 | if(NULL == p)
|
---|
446 | return;
|
---|
447 | if(p->tot_len != p->len) {
|
---|
448 | pbuf_free(p);
|
---|
449 | return;
|
---|
450 | }
|
---|
451 |
|
---|
452 | if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
|
---|
453 | return; /* not ready */
|
---|
454 | if (len < 2 || len < pkt[0] + 1)
|
---|
455 | return; /* too short */
|
---|
456 | clen = pkt[0];
|
---|
457 | nlen = len - (clen + 1);
|
---|
458 |
|
---|
459 | /* Null terminate and clean remote name. */
|
---|
460 | ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
|
---|
461 |
|
---|
462 | #if PPP_REMOTENAME
|
---|
463 | /* Microsoft doesn't send their name back in the PPP packet */
|
---|
464 | if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0))
|
---|
465 | strlcpy(rname, pcb->settings.remote_name, sizeof(rname));
|
---|
466 | #endif /* PPP_REMOTENAME */
|
---|
467 |
|
---|
468 | /* get secret for authenticating ourselves with the specified host */
|
---|
469 | if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) {
|
---|
470 | secret_len = 0; /* assume null secret if can't find one */
|
---|
471 | ppp_warn("No CHAP secret found for authenticating us to %q", rname);
|
---|
472 | }
|
---|
473 |
|
---|
474 | outp = (u_char*)p->payload;
|
---|
475 | MAKEHEADER(outp, PPP_CHAP);
|
---|
476 | outp += CHAP_HDRLEN;
|
---|
477 |
|
---|
478 | pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt,
|
---|
479 | secret, secret_len, pcb->chap_client.priv);
|
---|
480 | memset(secret, 0, secret_len);
|
---|
481 |
|
---|
482 | clen = *outp;
|
---|
483 | nlen = strlen(pcb->chap_client.name);
|
---|
484 | memcpy(outp + clen + 1, pcb->chap_client.name, nlen);
|
---|
485 |
|
---|
486 | outp = (u_char*)p->payload + PPP_HDRLEN;
|
---|
487 | len = CHAP_HDRLEN + clen + 1 + nlen;
|
---|
488 | outp[0] = CHAP_RESPONSE;
|
---|
489 | outp[1] = id;
|
---|
490 | outp[2] = len >> 8;
|
---|
491 | outp[3] = len;
|
---|
492 |
|
---|
493 | pbuf_realloc(p, PPP_HDRLEN + len);
|
---|
494 | ppp_write(pcb, p);
|
---|
495 | }
|
---|
496 |
|
---|
497 | static void chap_handle_status(ppp_pcb *pcb, int code, int id,
|
---|
498 | unsigned char *pkt, int len) {
|
---|
499 | const char *msg = NULL;
|
---|
500 | LWIP_UNUSED_ARG(id);
|
---|
501 |
|
---|
502 | if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
|
---|
503 | != (AUTH_STARTED|LOWERUP))
|
---|
504 | return;
|
---|
505 | pcb->chap_client.flags |= AUTH_DONE;
|
---|
506 |
|
---|
507 | if (code == CHAP_SUCCESS) {
|
---|
508 | /* used for MS-CHAP v2 mutual auth, yuck */
|
---|
509 | if (pcb->chap_client.digest->check_success != NULL) {
|
---|
510 | if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv))
|
---|
511 | code = CHAP_FAILURE;
|
---|
512 | } else
|
---|
513 | msg = "CHAP authentication succeeded";
|
---|
514 | } else {
|
---|
515 | if (pcb->chap_client.digest->handle_failure != NULL)
|
---|
516 | (*pcb->chap_client.digest->handle_failure)(pcb, pkt, len);
|
---|
517 | else
|
---|
518 | msg = "CHAP authentication failed";
|
---|
519 | }
|
---|
520 | if (msg) {
|
---|
521 | if (len > 0)
|
---|
522 | ppp_info("%s: %.*v", msg, len, pkt);
|
---|
523 | else
|
---|
524 | ppp_info("%s", msg);
|
---|
525 | }
|
---|
526 | if (code == CHAP_SUCCESS)
|
---|
527 | auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code);
|
---|
528 | else {
|
---|
529 | pcb->chap_client.flags |= AUTH_FAILED;
|
---|
530 | ppp_error("CHAP authentication failed");
|
---|
531 | auth_withpeer_fail(pcb, PPP_CHAP);
|
---|
532 | }
|
---|
533 | }
|
---|
534 |
|
---|
535 | static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) {
|
---|
536 | unsigned char code, id;
|
---|
537 | int len;
|
---|
538 |
|
---|
539 | if (pktlen < CHAP_HDRLEN)
|
---|
540 | return;
|
---|
541 | GETCHAR(code, pkt);
|
---|
542 | GETCHAR(id, pkt);
|
---|
543 | GETSHORT(len, pkt);
|
---|
544 | if (len < CHAP_HDRLEN || len > pktlen)
|
---|
545 | return;
|
---|
546 | len -= CHAP_HDRLEN;
|
---|
547 |
|
---|
548 | switch (code) {
|
---|
549 | case CHAP_CHALLENGE:
|
---|
550 | chap_respond(pcb, id, pkt, len);
|
---|
551 | break;
|
---|
552 | #if PPP_SERVER
|
---|
553 | case CHAP_RESPONSE:
|
---|
554 | chap_handle_response(pcb, id, pkt, len);
|
---|
555 | break;
|
---|
556 | #endif /* PPP_SERVER */
|
---|
557 | case CHAP_FAILURE:
|
---|
558 | case CHAP_SUCCESS:
|
---|
559 | chap_handle_status(pcb, code, id, pkt, len);
|
---|
560 | break;
|
---|
561 | default:
|
---|
562 | break;
|
---|
563 | }
|
---|
564 | }
|
---|
565 |
|
---|
566 | static void chap_protrej(ppp_pcb *pcb) {
|
---|
567 |
|
---|
568 | #if PPP_SERVER
|
---|
569 | if (pcb->chap_server.flags & TIMEOUT_PENDING) {
|
---|
570 | pcb->chap_server.flags &= ~TIMEOUT_PENDING;
|
---|
571 | UNTIMEOUT(chap_timeout, pcb);
|
---|
572 | }
|
---|
573 | if (pcb->chap_server.flags & AUTH_STARTED) {
|
---|
574 | pcb->chap_server.flags = 0;
|
---|
575 | auth_peer_fail(pcb, PPP_CHAP);
|
---|
576 | }
|
---|
577 | #endif /* PPP_SERVER */
|
---|
578 | if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
|
---|
579 | pcb->chap_client.flags &= ~AUTH_STARTED;
|
---|
580 | ppp_error("CHAP authentication failed due to protocol-reject");
|
---|
581 | auth_withpeer_fail(pcb, PPP_CHAP);
|
---|
582 | }
|
---|
583 | }
|
---|
584 |
|
---|
585 | #if PRINTPKT_SUPPORT
|
---|
586 | /*
|
---|
587 | * chap_print_pkt - print the contents of a CHAP packet.
|
---|
588 | */
|
---|
589 | static const char* const chap_code_names[] = {
|
---|
590 | "Challenge", "Response", "Success", "Failure"
|
---|
591 | };
|
---|
592 |
|
---|
593 | static int chap_print_pkt(const unsigned char *p, int plen,
|
---|
594 | void (*printer) (void *, const char *, ...), void *arg) {
|
---|
595 | int code, id, len;
|
---|
596 | int clen, nlen;
|
---|
597 | unsigned char x;
|
---|
598 |
|
---|
599 | if (plen < CHAP_HDRLEN)
|
---|
600 | return 0;
|
---|
601 | GETCHAR(code, p);
|
---|
602 | GETCHAR(id, p);
|
---|
603 | GETSHORT(len, p);
|
---|
604 | if (len < CHAP_HDRLEN || len > plen)
|
---|
605 | return 0;
|
---|
606 |
|
---|
607 | if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(chap_code_names))
|
---|
608 | printer(arg, " %s", chap_code_names[code-1]);
|
---|
609 | else
|
---|
610 | printer(arg, " code=0x%x", code);
|
---|
611 | printer(arg, " id=0x%x", id);
|
---|
612 | len -= CHAP_HDRLEN;
|
---|
613 | switch (code) {
|
---|
614 | case CHAP_CHALLENGE:
|
---|
615 | case CHAP_RESPONSE:
|
---|
616 | if (len < 1)
|
---|
617 | break;
|
---|
618 | clen = p[0];
|
---|
619 | if (len < clen + 1)
|
---|
620 | break;
|
---|
621 | ++p;
|
---|
622 | nlen = len - clen - 1;
|
---|
623 | printer(arg, " <");
|
---|
624 | for (; clen > 0; --clen) {
|
---|
625 | GETCHAR(x, p);
|
---|
626 | printer(arg, "%.2x", x);
|
---|
627 | }
|
---|
628 | printer(arg, ">, name = ");
|
---|
629 | ppp_print_string(p, nlen, printer, arg);
|
---|
630 | break;
|
---|
631 | case CHAP_FAILURE:
|
---|
632 | case CHAP_SUCCESS:
|
---|
633 | printer(arg, " ");
|
---|
634 | ppp_print_string(p, len, printer, arg);
|
---|
635 | break;
|
---|
636 | default:
|
---|
637 | for (clen = len; clen > 0; --clen) {
|
---|
638 | GETCHAR(x, p);
|
---|
639 | printer(arg, " %.2x", x);
|
---|
640 | }
|
---|
641 | /* no break */
|
---|
642 | }
|
---|
643 |
|
---|
644 | return len + CHAP_HDRLEN;
|
---|
645 | }
|
---|
646 | #endif /* PRINTPKT_SUPPORT */
|
---|
647 |
|
---|
648 | const struct protent chap_protent = {
|
---|
649 | PPP_CHAP,
|
---|
650 | chap_init,
|
---|
651 | chap_input,
|
---|
652 | chap_protrej,
|
---|
653 | chap_lowerup,
|
---|
654 | chap_lowerdown,
|
---|
655 | NULL, /* open */
|
---|
656 | NULL, /* close */
|
---|
657 | #if PRINTPKT_SUPPORT
|
---|
658 | chap_print_pkt,
|
---|
659 | #endif /* PRINTPKT_SUPPORT */
|
---|
660 | #if PPP_DATAINPUT
|
---|
661 | NULL, /* datainput */
|
---|
662 | #endif /* PPP_DATAINPUT */
|
---|
663 | #if PRINTPKT_SUPPORT
|
---|
664 | "CHAP", /* name */
|
---|
665 | NULL, /* data_name */
|
---|
666 | #endif /* PRINTPKT_SUPPORT */
|
---|
667 | #if PPP_OPTIONS
|
---|
668 | chap_option_list,
|
---|
669 | NULL, /* check_options */
|
---|
670 | #endif /* PPP_OPTIONS */
|
---|
671 | #if DEMAND_SUPPORT
|
---|
672 | NULL,
|
---|
673 | NULL
|
---|
674 | #endif /* DEMAND_SUPPORT */
|
---|
675 | };
|
---|
676 |
|
---|
677 | #endif /* PPP_SUPPORT && CHAP_SUPPORT */
|
---|