1/*
2 * Copyright (c) 2003-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * eapmschapv2_plugin.c
26 * - EAP-MSCHAPv2 plug-in
27 */
28
29/*
30 * Modification History
31 *
32 * May 21, 2003	Dieter Siegmund (dieter@apple)
33 * - created
34 */
35
36#include <EAP8021X/EAPClientPlugin.h>
37#include <EAP8021X/EAPClientProperties.h>
38#include <SystemConfiguration/SCValidation.h>
39#include <mach/boolean.h>
40#include <unistd.h>
41#include <stdlib.h>
42#include <string.h>
43#include <stdio.h>
44#include "EAPLog.h"
45#include <EAP8021X/EAP.h>
46#include <EAP8021X/EAPUtil.h>
47#include <EAP8021X/EAPClientModule.h>
48#include <EAP8021X/mschap.h>
49#include "myCFUtil.h"
50#include "nbo.h"
51#include "printdata.h"
52
53/*
54 * Declare these here to ensure that the compiler
55 * generates appropriate errors/warnings
56 */
57EAPClientPluginFuncIntrospect eapmschapv2_introspect;
58static EAPClientPluginFuncVersion eapmschapv2_version;
59static EAPClientPluginFuncEAPType eapmschapv2_type;
60static EAPClientPluginFuncEAPName eapmschapv2_name;
61static EAPClientPluginFuncInit eapmschapv2_init;
62static EAPClientPluginFuncFree eapmschapv2_free;
63static EAPClientPluginFuncProcess eapmschapv2_process;
64static EAPClientPluginFuncFreePacket eapmschapv2_free_packet;
65static EAPClientPluginFuncRequireProperties eapmschapv2_require_props;
66static EAPClientPluginFuncPublishProperties eapmschapv2_publish_props;
67static EAPClientPluginFuncSessionKey eapmschapv2_session_key;
68static EAPClientPluginFuncMasterSessionKeyCopyBytes eapmschapv2_msk_copy_bytes;
69static EAPClientPluginFuncServerKey eapmschapv2_server_key;
70static EAPClientPluginFuncCopyPacketDescription eapmschapv2_copy_packet_description;
71
72typedef struct {
73    NTPasswordBlock		encrypted_password;
74    uint8_t			encrypted_hash[NT_PASSWORD_HASH_SIZE];
75    uint8_t			peer_challenge[MSCHAP2_CHALLENGE_SIZE];
76    uint8_t			reserved[MSCHAP2_RESERVED_SIZE];
77    uint8_t			nt_response[MSCHAP_NT_RESPONSE_SIZE];
78    uint8_t			flags[2];
79} MSCHAPv2ChangePasswordResponse, * MSCHAPv2ChangePasswordResponseRef;
80
81enum {
82    kMSCHAPv2ChangePasswordVersion = 3
83};
84
85#define MSCHAP2_CHANGE_PASSWORD_RESPONSE_LENGTH sizeof(MSCHAPv2ChangePasswordResponse)
86
87enum {
88    kMSCHAPv2OpCodeChallenge = 1,
89    kMSCHAPv2OpCodeResponse = 2,
90    kMSCHAPv2OpCodeSuccess = 3,
91    kMSCHAPv2OpCodeFailure = 4,
92    kMSCHAPv2OpCodeChangePassword = 7
93};
94typedef uint8_t		MSCHAPv2OpCode;
95
96static const char *
97MSCHAPv2OpCodeStr(MSCHAPv2OpCode op_code)
98{
99    switch (op_code) {
100    case kMSCHAPv2OpCodeChallenge:
101	return ("Challenge");
102    case kMSCHAPv2OpCodeResponse:
103	return ("Response");
104    case kMSCHAPv2OpCodeSuccess:
105	return ("Success");
106    case kMSCHAPv2OpCodeFailure:
107	return ("Failure");
108    case kMSCHAPv2OpCodeChangePassword:
109	return ("ChangePassword");
110    default:
111	break;
112    }
113    return ("<unknown>");
114}
115
116/*
117 * EAP_MSCHAP2_MS_LENGTH_DIFFERENCE
118 *
119 * pkt.ms_length is always (pkt.length - 5) because ms_length is the number of
120 * bytes starting with the op_code field (offsetof(pkt.op_code) == 5).
121 */
122#define EAP_MSCHAP2_MS_LENGTH_DIFFERENCE	(sizeof(EAPRequestPacket))
123
124typedef struct EAPMSCHAPv2Packet_s {
125    uint8_t		code;
126    uint8_t		identifier;
127    uint8_t		length[2];	/* of entire request/response */
128    uint8_t		type;
129    uint8_t		op_code;
130    uint8_t		mschapv2_id;
131    uint8_t		ms_length[2];	/* must be pkt.length - 5 */
132    uint8_t		data[0];
133} EAPMSCHAPv2Packet, *EAPMSCHAPv2PacketRef;
134
135typedef struct EAPMSCHAPv2ChallengePacket_s {
136    uint8_t		code;
137    uint8_t		identifier;
138    uint8_t		length[2];	/* of entire request/response */
139    uint8_t		type;
140    uint8_t		op_code;
141    uint8_t		mschapv2_id;
142    uint8_t		ms_length[2];	/* pkt.length - 5 */
143    uint8_t		value_size;	/* MSCHAP2_CHALLENGE_SIZE */
144    uint8_t		challenge[MSCHAP2_CHALLENGE_SIZE];
145    uint8_t		name[0];
146} EAPMSCHAPv2ChallengePacket, *EAPMSCHAPv2ChallengePacketRef;
147
148typedef struct EAPMSCHAPv2ResponsePacket_s {
149    uint8_t		code;
150    uint8_t		identifier;
151    uint8_t		length[2];	/* of entire request/response */
152    uint8_t		type;
153    uint8_t		op_code;
154    uint8_t		mschapv2_id;
155    uint8_t		ms_length[2];	/* pkt.length - 5 */
156    uint8_t		value_size;	/* MSCHAP2_RESPONSE_LENGTH */
157    uint8_t		response[MSCHAP2_RESPONSE_LENGTH];
158    uint8_t		name[0];
159} EAPMSCHAPv2ResponsePacket, *EAPMSCHAPv2ResponsePacketRef;
160
161typedef struct EAPMSCHAPv2SuccessRequestPacket_s {
162    uint8_t		code;
163    uint8_t		identifier;
164    uint8_t		length[2];	/* of entire request/response */
165    uint8_t		type;
166    uint8_t		op_code;
167    uint8_t		mschapv2_id;
168    uint8_t		ms_length[2];	/* pkt.length - 5 */
169    uint8_t		auth_response[MSCHAP2_AUTH_RESPONSE_SIZE];
170    uint8_t		message[0];
171} EAPMSCHAPv2SuccessRequestPacket, *EAPMSCHAPv2SuccessRequestPacketRef;
172
173typedef struct EAPMSCHAPv2SuccessResponsePacket_s {
174    uint8_t		code;
175    uint8_t		identifier;
176    uint8_t		length[2];	/* of entire request/response */
177    uint8_t		type;
178    uint8_t		op_code;
179} EAPMSCHAPv2SuccessResponsePacket, *EAPMSCHAPv2SuccessResponsePacketRef;
180
181typedef struct EAPMSCHAPv2FailureRequestPacket_s {
182    uint8_t		code;
183    uint8_t		identifier;
184    uint8_t		length[2];	/* of entire request/response */
185    uint8_t		type;
186    uint8_t		op_code;
187    uint8_t		mschapv2_id;
188    uint8_t		ms_length[2];	/* pkt.length - 5 */
189    uint8_t		message[0];
190} EAPMSCHAPv2FailureRequestPacket, *EAPMSCHAPv2FailureRequestPacketRef;
191
192typedef struct EAPMSCHAPv2FailureResponsePacket_s {
193    uint8_t		code;
194    uint8_t		identifier;
195    uint8_t		length[2];	/* of entire request/response */
196    uint8_t		type;
197    uint8_t		op_code;
198} EAPMSCHAPv2FailureResponsePacket, *EAPMSCHAPv2FailureResponsePacketRef;
199
200typedef struct EAPMSCHAPv2ChangePasswordPacket_s {
201    uint8_t		code;
202    uint8_t		identifier;
203    uint8_t		length[2];	/* of entire request/response */
204    uint8_t		type;
205    uint8_t		op_code;
206    uint8_t		mschapv2_id;
207    uint8_t		ms_length[2];	/* pkt.length - 5 */
208    uint8_t		data[MSCHAP2_CHANGE_PASSWORD_RESPONSE_LENGTH];
209} EAPMSCHAPv2ChangePasswordPacket, *EAPMSCHAPv2ChangePasswordPacketRef;
210
211static __inline__ void
212EAPMSCHAPv2PacketSetMSLength(EAPMSCHAPv2PacketRef pkt, uint16_t length)
213{
214    net_uint16_set(pkt->ms_length, length);
215    return;
216}
217
218static __inline__ uint16_t
219EAPMSCHAPv2PacketGetMSLength(const EAPMSCHAPv2PacketRef pkt)
220{
221    return (net_uint16_get(pkt->ms_length));
222}
223
224typedef enum {
225    kMSCHAPv2ClientStateNone = 0,
226    kMSCHAPv2ClientStateResponseSent,
227    kMSCHAPv2ClientStateSuccessResponseSent,
228    kMSCHAPv2ClientStateChangePasswordSent,
229    kMSCHAPv2ClientStateSuccess,
230    kMSCHAPv2ClientStateFailure,
231} MSCHAPv2ClientState;
232
233typedef struct {
234    uint8_t			master_send_key[NT_SESSION_KEY_SIZE];
235    uint8_t			master_receive_key[NT_SESSION_KEY_SIZE];
236} eapmschap2_session_key;
237
238typedef struct {
239    MSCHAPv2ClientState		state;
240    EAPClientState		plugin_state;
241    bool			need_password;
242    bool			need_new_password;
243    uint32_t			last_generation;
244    uint8_t			peer_challenge[MSCHAP2_CHALLENGE_SIZE];
245    uint8_t			nt_response[MSCHAP_NT_RESPONSE_SIZE];
246    uint8_t			auth_challenge[MSCHAP2_CHALLENGE_SIZE];
247    eapmschap2_session_key	session_key;
248    bool			session_key_valid;
249    uint8_t			pkt_buffer[1024];
250} EAPMSCHAPv2PluginData, * EAPMSCHAPv2PluginDataRef;
251
252static void
253eapmschapv2_free_context(EAPMSCHAPv2PluginData * context)
254{
255    free(context);
256    return;
257}
258
259static void
260EAPMSCHAPv2PluginDataInit(EAPMSCHAPv2PluginDataRef context)
261{
262    context->state = kMSCHAPv2ClientStateNone;
263    context->plugin_state = kEAPClientStateAuthenticating;
264    context->need_password = FALSE;
265    context->need_new_password = FALSE;
266    context->session_key_valid = FALSE;
267    return;
268}
269
270static EAPClientStatus
271eapmschapv2_init(EAPClientPluginDataRef plugin, CFArrayRef * require_props,
272		 EAPClientDomainSpecificError * error)
273{
274    EAPMSCHAPv2PluginDataRef	context = NULL;
275    EAPClientStatus		result = kEAPClientStatusOK;
276
277    *error = 0;
278    *require_props = NULL;
279    context = malloc(sizeof(*context));
280    plugin->private = context;
281    EAPMSCHAPv2PluginDataInit(context);
282    context->last_generation = plugin->generation;
283    return (result);
284}
285
286static void
287eapmschapv2_free(EAPClientPluginDataRef plugin)
288{
289    EAPMSCHAPv2PluginDataRef context;
290
291    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
292    if (context != NULL) {
293	eapmschapv2_free_context(context);
294	plugin->private = NULL;
295    }
296    return;
297}
298
299static void
300eapmschapv2_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
301{
302    EAPMSCHAPv2PluginDataRef context;
303
304    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
305    if ((void*)arg != context->pkt_buffer) {
306	free(arg);
307    }
308    return;
309}
310
311static EAPMSCHAPv2ResponsePacketRef
312EAPMSCHAPv2ResponsePacketCreate(EAPClientPluginDataRef plugin,
313				int identifier, int mschapv2_id,
314				EAPClientStatus * client_status)
315{
316    CFDataRef				client_challenge;
317    EAPMSCHAPv2PluginDataRef 		context;
318    EAPMSCHAPv2ResponsePacketRef	out_pkt_p;
319    MSCHAP2ResponseRef			resp_p;
320    int					out_length;
321
322    /* check for out-of-band client challenge */
323    client_challenge
324	= CFDictionaryGetValue(plugin->properties,
325			       kEAPClientPropEAPMSCHAPv2ClientChallenge);
326    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
327    out_length = sizeof(*out_pkt_p) + plugin->username_length;
328    out_pkt_p = (EAPMSCHAPv2ResponsePacketRef)
329	EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer),
330			kEAPCodeResponse, identifier,
331			kEAPTypeMSCHAPv2, NULL,
332			out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE,
333			NULL);
334
335    if (client_challenge != NULL) {
336	if (CFDataGetLength(client_challenge)
337	    != sizeof(context->peer_challenge)) {
338	    EAPLOG(LOG_NOTICE,
339		   "EAPMSCHAPv2ResponsePacketCreate: internal error %ld != %ld",
340		   CFDataGetLength(client_challenge),
341		   sizeof(context->peer_challenge));
342	    if (client_status != NULL) {
343		*client_status = kEAPClientStatusInternalError;
344	    }
345	    context->plugin_state = kEAPClientStateFailure;
346	    return (NULL);
347	}
348	memcpy(context->peer_challenge, CFDataGetBytePtr(client_challenge),
349	       sizeof(context->peer_challenge));
350    }
351    else {
352	MSChapFillWithRandom(context->peer_challenge,
353			     sizeof(context->peer_challenge));
354    }
355    MSChap2(context->auth_challenge, context->peer_challenge,
356	    plugin->username, plugin->password, plugin->password_length,
357	    context->nt_response);
358
359    /* fill in the data */
360    out_pkt_p->op_code = kMSCHAPv2OpCodeResponse;
361    out_pkt_p->mschapv2_id = mschapv2_id;
362    EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p,
363				 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE);
364    out_pkt_p->value_size = sizeof(out_pkt_p->response);
365    resp_p = (MSCHAP2ResponseRef)out_pkt_p->response;
366    if (client_challenge == NULL) {
367	memcpy(resp_p->peer_challenge, context->peer_challenge,
368	       sizeof(context->peer_challenge));
369    }
370    else {
371	memset(resp_p->peer_challenge, 0, sizeof(resp_p->peer_challenge));
372    }
373    memset(resp_p->reserved, 0, sizeof(resp_p->reserved));
374    memcpy(resp_p->nt_response, context->nt_response,
375	   sizeof(context->nt_response));
376    resp_p->flags[0] = 0;
377    memcpy(out_pkt_p->name, plugin->username, plugin->username_length);
378    return (out_pkt_p);
379}
380
381static EAPMSCHAPv2PacketRef
382eapmschapv2_challenge(EAPClientPluginDataRef plugin,
383		      EAPMSCHAPv2PacketRef in_pkt_p,
384		      uint16_t in_length,
385		      EAPClientStatus * client_status,
386		      EAPClientDomainSpecificError * error)
387{
388    EAPMSCHAPv2PluginDataRef 		context;
389    EAPMSCHAPv2ChallengePacketRef	challenge_p;
390    EAPMSCHAPv2ResponsePacketRef	out_pkt_p;
391    CFDataRef				server_challenge;
392
393    if (in_length < sizeof(*challenge_p)) {
394	EAPLOG(LOG_NOTICE, "eapmschapv2_challenge: length %d < %ld",
395	       in_length, sizeof(*challenge_p));
396	goto done;
397    }
398    challenge_p = (EAPMSCHAPv2ChallengePacketRef)in_pkt_p;
399    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
400
401    /* if we don't have a password, ask for one */
402    EAPMSCHAPv2PluginDataInit(context);
403    if (plugin->password == NULL) {
404	context->need_password = TRUE;
405	*client_status = kEAPClientStatusUserInputRequired;
406	goto done;
407    }
408
409    /* check for out-of-band server challenge */
410    server_challenge
411	= CFDictionaryGetValue(plugin->properties,
412			       kEAPClientPropEAPMSCHAPv2ServerChallenge);
413    if (server_challenge != NULL) {
414	if (CFDataGetLength(server_challenge)
415	    != sizeof(context->auth_challenge)) {
416	    EAPLOG(LOG_NOTICE,
417		   "eapmschapv2_challenge: internal error %ld != %ld",
418		   CFDataGetLength(server_challenge),
419		   sizeof(context->auth_challenge));
420	    *client_status = kEAPClientStatusInternalError;
421	    context->plugin_state = kEAPClientStateFailure;
422	    goto done;
423	}
424	memcpy(context->auth_challenge, CFDataGetBytePtr(server_challenge),
425	       sizeof(context->auth_challenge));
426    }
427    else {
428	/* remember the auth challenge for later */
429	memcpy(context->auth_challenge, challenge_p->challenge,
430	       sizeof(context->auth_challenge));
431    }
432    out_pkt_p = EAPMSCHAPv2ResponsePacketCreate(plugin, in_pkt_p->identifier,
433						challenge_p->mschapv2_id,
434						client_status);
435    if (out_pkt_p == NULL) {
436	goto done;
437    }
438    context->state = kMSCHAPv2ClientStateResponseSent;
439    return ((EAPMSCHAPv2PacketRef)out_pkt_p);
440
441 done:
442    return (NULL);
443}
444
445static void
446eapmschapv2_compute_session_key(EAPMSCHAPv2PluginDataRef context,
447				const char * password,
448				int password_length)
449{
450    uint8_t				master_key[NT_MASTER_KEY_SIZE];
451
452    MSChap2_MPPEGetMasterKey((const uint8_t *)password, password_length,
453			     context->nt_response,
454			     master_key);
455    /* MasterSendKey */
456    MSChap2_MPPEGetAsymetricStartKey(master_key,
457				     context->session_key.master_send_key,
458				     NT_SESSION_KEY_SIZE,
459				     TRUE, TRUE);
460    /* MasterReceiveKey */
461    MSChap2_MPPEGetAsymetricStartKey(master_key,
462				     context->session_key.master_receive_key,
463				     NT_SESSION_KEY_SIZE,
464				     FALSE, TRUE);
465    context->session_key_valid = TRUE;
466    return;
467}
468
469static EAPMSCHAPv2PacketRef
470eapmschapv2_success_request(EAPClientPluginDataRef plugin,
471			    EAPMSCHAPv2PacketRef in_pkt_p,
472			    uint16_t in_length,
473			    EAPClientStatus * client_status,
474			    EAPClientDomainSpecificError * error)
475{
476    EAPMSCHAPv2PluginDataRef 		context;
477    char *				new_password = NULL;
478    CFStringRef				new_password_cf;
479    EAPMSCHAPv2SuccessResponsePacketRef	out_pkt_p = NULL;
480    const char *			password;
481    int					password_length;
482    EAPMSCHAPv2SuccessRequestPacketRef	r_p;
483    Boolean				valid;
484
485    if (in_length < sizeof(*r_p)) {
486	EAPLOG_FL(LOG_NOTICE, "length %d < %ld", in_length, sizeof(*r_p));
487	goto done;
488    }
489    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
490    switch (context->state) {
491    case kMSCHAPv2ClientStateChangePasswordSent:
492	new_password_cf
493	    = CFDictionaryGetValue(plugin->properties,
494				   kEAPClientPropNewPassword);
495	if (new_password_cf == NULL) {
496	    EAPLOG_FL(LOG_NOTICE, "NewPassword is missing");
497	    goto done;
498	}
499	new_password = my_CFStringToCString(new_password_cf,
500					    kCFStringEncodingUTF8);
501	password = new_password;
502	password_length = (int)strlen(new_password);
503	break;
504    case kMSCHAPv2ClientStateResponseSent:
505    case kMSCHAPv2ClientStateSuccess:
506	password = (const char *)plugin->password;
507	password_length = plugin->password_length;
508	break;
509    case kMSCHAPv2ClientStateFailure:
510    default:
511	goto done;
512    }
513    /* process success request */
514    r_p = (EAPMSCHAPv2SuccessRequestPacketRef)in_pkt_p;
515    valid = MSChap2AuthResponseValid((const uint8_t *)password,
516				     password_length,
517				     context->nt_response,
518				     context->peer_challenge,
519				     context->auth_challenge,
520				     (const uint8_t *)plugin->username,
521				     r_p->auth_response);
522    if (valid == FALSE) {
523	EAPLOG(LOG_NOTICE,
524	       "eapmschapv2_success_request: invalid server auth response");
525	context->plugin_state = kEAPClientStateFailure;
526	context->state = kMSCHAPv2ClientStateFailure;
527	*client_status = kEAPClientStatusFailed;
528	goto done;
529    }
530    switch (context->state) {
531    case kMSCHAPv2ClientStateResponseSent:
532	EAPLOG(LOG_DEBUG,
533	       "eapmschapv2_success_request: successfully authenticated");
534	break;
535    case kMSCHAPv2ClientStateChangePasswordSent:
536	EAPLOG(LOG_NOTICE,
537	       "eapmschapv2_success_request: change password succeeded");
538	break;
539    default:
540	break;
541    }
542    eapmschapv2_compute_session_key(context, password, password_length);
543    context->state = kMSCHAPv2ClientStateSuccess;
544    out_pkt_p = (EAPMSCHAPv2SuccessResponsePacketRef)
545	EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer),
546			kEAPCodeResponse, in_pkt_p->identifier,
547			kEAPTypeMSCHAPv2, NULL,
548			sizeof(*out_pkt_p) - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE,
549			NULL);
550    out_pkt_p->op_code = kMSCHAPv2OpCodeSuccess;
551 done:
552    if (new_password != NULL) {
553	free(new_password);
554    }
555    return ((EAPMSCHAPv2PacketRef)out_pkt_p);
556}
557
558
559enum {
560    kMSCHAP2FailureMessageFlagError = 0x1,
561    kMSCHAP2FailureMessageFlagRetry = 0x2,
562    kMSCHAP2FailureMessageFlagChallenge = 0x4,
563    kMSCHAP2FailureMessageFlagVersion = 0x8,
564    kMSCHAP2FailureMessageFlagMessage = 0x10,
565};
566
567static bool
568mschap2_message_int32_attr(const char * message, uint16_t message_length,
569			   const char * attr, int attr_len,
570			   int32_t * int_value)
571{
572    bool	present = FALSE;
573    char *	val;
574
575    val = strnstr(message, attr, message_length);
576    if (val != NULL && message_length > attr_len) {
577	val += attr_len;
578
579	*int_value = (int)strtol(val, NULL, 10);
580	present = TRUE;
581    }
582    return (present);
583}
584
585static bool
586mschap2_message_challenge_attr(const char * message, uint16_t message_length,
587			       const char * attr,  int attr_len,
588			       uint8_t challenge[MSCHAP2_CHALLENGE_SIZE])
589{
590    int		i;
591    bool	present = FALSE;
592    char *	val;
593
594    val = strnstr(message, attr, message_length);
595    if (val != NULL
596	&& message_length > (attr_len + MSCHAP2_CHALLENGE_SIZE * 2)) {
597	char str[3];
598
599	str[2] = '\0'; /* nul-terminate */
600	val += attr_len;
601	for (i = 0; i < MSCHAP2_CHALLENGE_SIZE; i++) {
602	    str[0] = val[0];
603	    str[1] = val[1];
604	    challenge[i] = strtoul(str, NULL, 16);
605	    val += 2;
606	}
607	present = TRUE;
608    }
609    return (present);
610}
611
612static EAPMSCHAPv2ChangePasswordPacketRef
613EAPMSCHAPv2ChangePasswordPacketCreate(EAPClientPluginDataRef plugin,
614				      int identifier, int mschapv2_id,
615				      const char * new_password,
616				      int new_password_length,
617				      EAPClientStatus * client_status)
618{
619    MSCHAPv2ChangePasswordResponseRef	change_p;
620    EAPMSCHAPv2PluginDataRef 		context;
621    EAPMSCHAPv2ChangePasswordPacketRef	out_pkt_p;
622    int					out_length;
623
624    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
625    out_length = sizeof(*out_pkt_p);
626    out_pkt_p = (EAPMSCHAPv2ChangePasswordPacketRef)
627	EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer),
628			kEAPCodeResponse, identifier,
629			kEAPTypeMSCHAPv2, NULL,
630			out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE,
631			NULL);
632    MSChapFillWithRandom(context->peer_challenge,
633			 sizeof(context->peer_challenge));
634    /* compute nt_response using challenge from error packet and new password */
635    MSChap2(context->auth_challenge, context->peer_challenge,
636	    plugin->username,
637	    (const uint8_t *)new_password, new_password_length,
638	    context->nt_response);
639
640    /* fill in the packet */
641    out_pkt_p->op_code = kMSCHAPv2OpCodeChangePassword;
642    out_pkt_p->mschapv2_id = mschapv2_id;
643    EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p,
644				 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE);
645    change_p = (MSCHAPv2ChangePasswordResponseRef)out_pkt_p->data;
646    memcpy(change_p->peer_challenge, context->peer_challenge,
647	   sizeof(context->peer_challenge));
648    memset(change_p->reserved, 0, sizeof(change_p->reserved));
649    memcpy(change_p->nt_response, context->nt_response,
650	   sizeof(context->nt_response));
651    NTPasswordBlockEncryptNewPasswordWithOldHash((const uint8_t *)new_password,
652						 new_password_length,
653						 plugin->password,
654						 plugin->password_length,
655						 &change_p->encrypted_password);
656    NTPasswordHashEncryptOldWithNew((const uint8_t *)new_password,
657				    new_password_length,
658				    plugin->password,
659				    plugin->password_length,
660				    change_p->encrypted_hash);
661    change_p->flags[0] = 0;
662    change_p->flags[1] = 0;
663    return (out_pkt_p);
664}
665
666static uint8_t
667MSCHAPv2ParseFailureMessage(const char * message,
668			    uint16_t message_length,
669			    int32_t * error, int32_t * retry,
670			    uint8_t challenge[MSCHAP2_CHALLENGE_SIZE],
671			    int32_t * version, char * * ret_message)
672{
673    uint32_t	flags = 0;
674    char *	val;
675
676    /*
677     * Parse the message string format:
678     *   E=eeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=mm...
679     * We don't assume that they are in any particular order, since that's
680     * the most flexible.
681     */
682    if (mschap2_message_int32_attr(message, message_length, "E=", 2, error)) {
683	flags |= kMSCHAP2FailureMessageFlagError;
684    }
685    if (mschap2_message_int32_attr(message, message_length, "R=", 2, retry)) {
686	flags |= kMSCHAP2FailureMessageFlagRetry;
687    }
688    if (mschap2_message_int32_attr(message, message_length, "V=", 2, version)) {
689	flags |= kMSCHAP2FailureMessageFlagVersion;
690    }
691    if (mschap2_message_challenge_attr(message, message_length,
692				       "C=", 2, challenge)) {
693	flags |= kMSCHAP2FailureMessageFlagChallenge;
694    }
695    val = strnstr(message, "M=", message_length);
696    if (val != NULL) {
697	*ret_message = val + 2;
698	flags |= kMSCHAP2FailureMessageFlagMessage;
699    }
700    return (flags);
701}
702
703static EAPMSCHAPv2PacketRef
704eapmschapv2_failure_request(EAPClientPluginDataRef plugin,
705			    EAPMSCHAPv2PacketRef in_pkt_p,
706			    uint16_t in_length,
707			    EAPClientStatus * client_status,
708			    EAPClientDomainSpecificError * error)
709{
710    EAPMSCHAPv2PluginDataRef 		context;
711    bool				handled = FALSE;
712    char *				message = NULL;
713    int					message_length;
714    uint32_t				flags = 0;
715    int32_t				r_error;
716    int32_t				r_retry = 0;
717    int32_t				r_version = 0;
718    char *				r_message;
719    EAPMSCHAPv2FailureRequestPacketRef	r_p;
720    EAPMSCHAPv2PacketRef		out_pkt_p = NULL;
721
722    if (in_length < sizeof(*r_p)) {
723	EAPLOG(LOG_NOTICE, "eapmschapv2_failure_request: length %d < %ld",
724	       in_length, sizeof(*r_p));
725	goto done;
726    }
727    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
728    switch (context->state) {
729    case kMSCHAPv2ClientStateResponseSent:
730    case kMSCHAPv2ClientStateChangePasswordSent:
731	break;
732    case kMSCHAPv2ClientStateFailure:
733	break;
734    case kMSCHAPv2ClientStateSuccess:
735    default:
736	goto done;
737    }
738    r_p = (EAPMSCHAPv2FailureRequestPacketRef)in_pkt_p;
739    do { /* something to break out of */
740	/* allocate a message buffer that's guaranteed to be nul-terminated */
741	message_length = in_length - sizeof(*r_p);
742	if (message_length == 0) {
743	    break;
744	}
745	message = malloc(message_length + 1);
746	memcpy(message, r_p->message, message_length);
747	message[message_length] = '\0';
748
749	/* parse it */
750	flags = MSCHAPv2ParseFailureMessage(message, message_length,
751					    &r_error, &r_retry,
752					    context->auth_challenge,
753					    &r_version, &r_message);
754
755	if ((flags & kMSCHAP2FailureMessageFlagError) == 0) {
756	    break;
757	}
758	EAPLOG(LOG_NOTICE, "MSCHAPv2 Error = %d, Retry = %d, Version = %d",
759	       r_error, r_retry, r_version);
760
761	if ((flags & kMSCHAP2FailureMessageFlagChallenge) == 0) {
762	    EAPLOG(LOG_NOTICE,
763		   "MSCHAPv2 Failure Request does not contain challenge");
764	    break;
765	}
766	if (r_error != kMSCHAP2_ERROR_PASSWD_EXPIRED) {
767	    if (r_retry == 0) {
768		/* can't retry, acknowledge the error */
769		break;
770	    }
771	    if (context->last_generation == plugin->generation
772		|| plugin->password == NULL) {
773		context->need_password = TRUE;
774		*client_status = kEAPClientStatusUserInputRequired;
775	    }
776	    else {
777		out_pkt_p = (EAPMSCHAPv2PacketRef)
778		    EAPMSCHAPv2ResponsePacketCreate(plugin,
779						    r_p->identifier,
780						    r_p->mschapv2_id + 1,
781						    NULL);
782		if (out_pkt_p == NULL) {
783		    break;
784		}
785		context->state = kMSCHAPv2ClientStateResponseSent;
786	    }
787	}
788	else {
789	    char *	new_password = NULL;
790
791	    if (r_version != kMSCHAPv2ChangePasswordVersion) {
792		break;
793	    }
794	    if (plugin->password == NULL) {
795		break;
796	    }
797	    if (context->last_generation != plugin->generation
798		&& plugin->properties != NULL) {
799		CFStringRef		new_password_cf;
800
801		new_password_cf
802		    = CFDictionaryGetValue(plugin->properties,
803					   kEAPClientPropNewPassword);
804		if (new_password_cf != NULL) {
805		    new_password = my_CFStringToCString(new_password_cf,
806							kCFStringEncodingUTF8);
807		}
808	    }
809	    if (new_password != NULL) {
810		out_pkt_p = (EAPMSCHAPv2PacketRef)
811		    EAPMSCHAPv2ChangePasswordPacketCreate(plugin,
812							  r_p->identifier,
813							  r_p->mschapv2_id + 1,
814							  new_password,
815							  (int)
816							  strlen(new_password),
817							  NULL);
818		free(new_password);
819		if (out_pkt_p == NULL) {
820		    break;
821		}
822		context->state = kMSCHAPv2ClientStateChangePasswordSent;
823	    }
824	    else {
825		context->need_new_password = TRUE;
826		*client_status = kEAPClientStatusUserInputRequired;
827	    }
828	}
829	handled = TRUE;
830    } while (0); /* something to break out of */
831
832    if (handled == FALSE) {
833	context->state = kMSCHAPv2ClientStateFailure;
834	context->plugin_state = kEAPClientStateFailure;
835
836	*client_status = kEAPClientStatusFailed;
837	out_pkt_p = (EAPMSCHAPv2PacketRef)
838	    EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer),
839			    kEAPCodeResponse, in_pkt_p->identifier,
840			    kEAPTypeMSCHAPv2, NULL,
841			    sizeof(*out_pkt_p) - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE,
842			    NULL);
843	out_pkt_p->op_code = kMSCHAPv2OpCodeFailure;
844	out_pkt_p->mschapv2_id = r_p->mschapv2_id;
845	EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p,
846				     (sizeof(*out_pkt_p)
847				      - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
848    }
849    if (message != NULL) {
850	free(message);
851    }
852    return (out_pkt_p);
853
854 done:
855    if (out_pkt_p != NULL) {
856	free(out_pkt_p);
857    }
858    if (message != NULL) {
859	free(message);
860    }
861    return (NULL);
862}
863
864static EAPPacketRef
865eapmschapv2_request(EAPClientPluginDataRef plugin,
866		    const EAPPacketRef in_pkt,
867		    EAPClientStatus * client_status,
868		    EAPClientDomainSpecificError * error)
869{
870    EAPMSCHAPv2PacketRef	mschap_in_p;
871    EAPMSCHAPv2PacketRef	mschap_out_p = NULL;
872    uint16_t			in_length = EAPPacketGetLength(in_pkt);
873
874    mschap_in_p = (EAPMSCHAPv2PacketRef)in_pkt;
875    if (in_length < sizeof(*mschap_in_p)) {
876	EAPLOG(LOG_NOTICE, "eapmschapv2_request: length %d < %ld",
877	       in_length, sizeof(*mschap_in_p));
878	goto done;
879    }
880    switch (mschap_in_p->op_code) {
881    case kMSCHAPv2OpCodeChallenge:
882	mschap_out_p = eapmschapv2_challenge(plugin, mschap_in_p, in_length,
883					     client_status, error);
884	break;
885    case kMSCHAPv2OpCodeResponse:
886	/* ignore */
887	break;
888    case kMSCHAPv2OpCodeSuccess:
889	mschap_out_p = eapmschapv2_success_request(plugin, mschap_in_p,
890						   in_length, client_status,
891						   error);
892	break;
893    case kMSCHAPv2OpCodeFailure:
894	mschap_out_p = eapmschapv2_failure_request(plugin, mschap_in_p,
895						   in_length, client_status,
896						   error);
897	break;
898    default:
899	break;
900    }
901    return ((EAPPacketRef)mschap_out_p);
902
903 done:
904    if (mschap_out_p != NULL) {
905	free(mschap_out_p);
906    }
907    return (NULL);
908}
909
910static EAPClientState
911eapmschapv2_process(EAPClientPluginDataRef plugin,
912		    const EAPPacketRef in_pkt,
913		    EAPPacketRef * out_pkt_p,
914		    EAPClientStatus * client_status,
915		    EAPClientDomainSpecificError * error)
916{
917    EAPMSCHAPv2PluginDataRef 	context;
918
919    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
920
921    *client_status = kEAPClientStatusOK;
922    *error = 0;
923    *out_pkt_p = NULL;
924    switch (in_pkt->code) {
925    case kEAPCodeRequest:
926	*out_pkt_p = eapmschapv2_request(plugin, in_pkt, client_status,
927					 error);
928	break;
929    case kEAPCodeSuccess:
930	if (context->state == kMSCHAPv2ClientStateSuccess) {
931	    context->plugin_state = kEAPClientStateSuccess;
932	}
933	break;
934    case kEAPCodeFailure:
935	if (context->state == kMSCHAPv2ClientStateFailure) {
936	    context->plugin_state = kEAPClientStateFailure;
937	}
938	break;
939    case kEAPCodeResponse:
940    default:
941	/* ignore */
942	break;
943    }
944    context->last_generation = plugin->generation;
945    return (context->plugin_state);
946}
947
948static CFDictionaryRef
949eapmschapv2_publish_props(EAPClientPluginDataRef plugin)
950{
951    return (NULL);
952}
953
954static CFArrayRef
955eapmschapv2_require_props(EAPClientPluginDataRef plugin)
956{
957    CFMutableArrayRef 		array = NULL;
958    EAPMSCHAPv2PluginDataRef	context;
959
960    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
961    if (context->need_password || context->need_new_password) {
962	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
963	if (context->need_password) {
964	    CFArrayAppendValue(array, kEAPClientPropUserPassword);
965	}
966	if (context->need_new_password) {
967	    CFArrayAppendValue(array, kEAPClientPropNewPassword);
968	}
969    }
970    return (array);
971}
972
973static bool
974eapmschapv2_challenge_append(CFMutableStringRef str, const EAPPacketRef pkt,
975			     int length)
976{
977    EAPMSCHAPv2ChallengePacketRef	challenge_p;
978    int					name_length;
979    bool				packet_is_valid = FALSE;
980
981    if (length < sizeof(*challenge_p)) {
982	STRING_APPEND(str, "Error: length %d < %d\n", length,
983		      (int)sizeof(*challenge_p));
984	goto done;
985    }
986    challenge_p = (EAPMSCHAPv2ChallengePacketRef)pkt;
987    STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d Value-Size %d\n",
988		  challenge_p->mschapv2_id,
989		  EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt),
990		  challenge_p->value_size);
991
992    if (challenge_p->value_size != sizeof(challenge_p->challenge)) {
993	STRING_APPEND(str, "Error: Value-Size should be %d\n",
994		      (int)sizeof(challenge_p->challenge));
995    }
996    if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)
997	!= (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) {
998	STRING_APPEND(str, "Error: MS-Length should be %d\n",
999		      (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
1000	goto done;
1001    }
1002    STRING_APPEND(str, "Challenge: ");
1003    print_bytes_cfstr(str, challenge_p->challenge,
1004		      sizeof(challenge_p->challenge));
1005    STRING_APPEND(str, "\n");
1006    name_length = length - sizeof(*challenge_p);
1007    if (name_length > 0) {
1008	STRING_APPEND(str, "Name: %.*s\n", name_length, challenge_p->name);
1009    }
1010    packet_is_valid = TRUE;
1011
1012 done:
1013    return (packet_is_valid);
1014}
1015
1016static bool
1017eapmschapv2_response_append(CFMutableStringRef str, const EAPPacketRef pkt,
1018			    int length)
1019{
1020    int					name_length;
1021    bool				packet_is_valid = FALSE;
1022    EAPMSCHAPv2ResponsePacketRef	r_p;
1023    MSCHAP2ResponseRef			resp_p;
1024
1025    if (length < sizeof(*r_p)) {
1026	STRING_APPEND(str, "Error: length %d < %d\n", length,
1027		      (int)sizeof(*r_p));
1028	goto done;
1029    }
1030    r_p = (EAPMSCHAPv2ResponsePacketRef)pkt;
1031    STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d Value-Size %d\n",
1032		  r_p->mschapv2_id,
1033		  EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt),
1034		  r_p->value_size);
1035
1036    if (r_p->value_size != sizeof(r_p->response)) {
1037	STRING_APPEND(str, "Error: Value-Size should be %d\n",
1038		      (int)sizeof(r_p->response));
1039    }
1040    if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)
1041	!= (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) {
1042	STRING_APPEND(str, "Error: MS-Length should be %d\n",
1043		      (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
1044	goto done;
1045    }
1046    resp_p = (MSCHAP2ResponseRef)r_p->response;
1047    STRING_APPEND(str, "Response:\n");
1048    STRING_APPEND(str,
1049		  "Peer Challenge: ");
1050    print_bytes_cfstr(str, resp_p->peer_challenge,
1051		      sizeof(resp_p->peer_challenge));
1052    STRING_APPEND(str, "\n"
1053		  "Reserved:       ");
1054    print_bytes_cfstr(str, resp_p->reserved, sizeof(resp_p->reserved));
1055    STRING_APPEND(str, "\n"
1056		  "NT Response:    ");
1057    print_bytes_cfstr(str, resp_p->nt_response, sizeof(resp_p->nt_response));
1058    STRING_APPEND(str, "\n"
1059		  "Flags:          ");
1060    print_bytes_cfstr(str, resp_p->flags, sizeof(resp_p->flags));
1061    name_length = length - sizeof(*r_p);
1062    if (name_length > 0) {
1063	STRING_APPEND(str, "\n"
1064		      "Name:           %.*s\n", name_length, r_p->name);
1065    }
1066    packet_is_valid = TRUE;
1067
1068 done:
1069    return (packet_is_valid);
1070}
1071
1072static bool
1073eapmschapv2_success_request_append(CFMutableStringRef str,
1074				   const EAPPacketRef pkt, int length)
1075{
1076    int					message_length;
1077    EAPMSCHAPv2SuccessRequestPacketRef	r_p;
1078    bool				packet_is_valid = FALSE;
1079
1080    if (length < sizeof(*r_p)) {
1081	STRING_APPEND(str,
1082		      "Error: length %d < %d\n", length, (int)sizeof(*r_p));
1083	goto done;
1084    }
1085    r_p = (EAPMSCHAPv2SuccessRequestPacketRef)pkt;
1086    STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n",
1087		  r_p->mschapv2_id,
1088		  EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt));
1089    if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)
1090	!= (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) {
1091	STRING_APPEND(str, "Error: MS-Length should be %d\n",
1092		      (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
1093	goto done;
1094    }
1095    STRING_APPEND(str,
1096		  "Auth Response: %.*s\n", (int)sizeof(r_p->auth_response),
1097		  r_p->auth_response);
1098    message_length = length - sizeof(*r_p);
1099    if (message_length > 1) {
1100	/* skip the space when we print it out */
1101	STRING_APPEND(str,
1102		      "Message:       %.*s\n", message_length - 1,
1103		      r_p->message + 1);
1104    }
1105    packet_is_valid = TRUE;
1106
1107 done:
1108    return (packet_is_valid);
1109}
1110
1111static bool
1112eapmschapv2_failure_request_append(CFMutableStringRef str,
1113				   const EAPPacketRef pkt, int length)
1114{
1115    int					message_length;
1116    bool				packet_is_valid = FALSE;
1117    EAPMSCHAPv2FailureRequestPacketRef	r_p;
1118
1119    if (length < sizeof(*r_p)) {
1120	STRING_APPEND(str,
1121		      "Error: length %d < %d\n", length, (int)sizeof(*r_p));
1122	goto done;
1123    }
1124    r_p = (EAPMSCHAPv2FailureRequestPacketRef)pkt;
1125    STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n",
1126		  r_p->mschapv2_id,
1127		  EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt));
1128    if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)
1129	!= (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) {
1130	STRING_APPEND(str, "Error: MS-Length should be %d\n",
1131		      (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
1132	goto done;
1133    }
1134    message_length = length - sizeof(*r_p);
1135    if (message_length > 0) {
1136	STRING_APPEND(str,
1137		      "Message:       %.*s\n", message_length, r_p->message);
1138    }
1139    packet_is_valid = TRUE;
1140
1141 done:
1142    return (packet_is_valid);
1143}
1144
1145static bool
1146eapmschapv2_change_password_append(CFMutableStringRef str,
1147				   const EAPPacketRef pkt, int length)
1148{
1149    MSCHAPv2ChangePasswordResponseRef	change_p;
1150    bool				packet_is_valid = FALSE;
1151    EAPMSCHAPv2ChangePasswordPacketRef	r_p;
1152
1153    if (length < sizeof(*r_p)) {
1154	STRING_APPEND(str,
1155		      "Error: length %d < %d\n", length, (int)sizeof(*r_p));
1156	goto done;
1157    }
1158    r_p = (EAPMSCHAPv2ChangePasswordPacketRef)pkt;
1159    STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n",
1160		  r_p->mschapv2_id,
1161		  EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt));
1162    if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)
1163	!= (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) {
1164	STRING_APPEND(str, "Error: MS-Length should be %d\n",
1165		      (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE));
1166	goto done;
1167    }
1168    change_p = (MSCHAPv2ChangePasswordResponseRef)(r_p->data);
1169    STRING_APPEND(str, "Encrypted Password:\n");
1170    print_data_cfstr(str, (void *)&change_p->encrypted_password,
1171		     sizeof(change_p->encrypted_password));
1172    STRING_APPEND(str, "Encrypted Hash: ");
1173    print_bytes_cfstr(str, change_p->encrypted_hash,
1174		      sizeof(change_p->encrypted_hash));
1175    STRING_APPEND(str, "\n");
1176    STRING_APPEND(str, "Peer Challenge: ");
1177    print_bytes_cfstr(str, change_p->peer_challenge,
1178		      sizeof(change_p->peer_challenge));
1179    STRING_APPEND(str, "\n");
1180    STRING_APPEND(str, "Reserved: ");
1181    print_bytes_cfstr(str, change_p->reserved,
1182		      sizeof(change_p->reserved));
1183    STRING_APPEND(str, "\n");
1184    STRING_APPEND(str, "NT Response: ");
1185    print_bytes_cfstr(str, change_p->nt_response,
1186		      sizeof(change_p->nt_response));
1187    STRING_APPEND(str, "\n");
1188    STRING_APPEND(str, "NT Flags: ");
1189    print_bytes_cfstr(str, change_p->flags,
1190		      sizeof(change_p->flags));
1191    STRING_APPEND(str, "\n");
1192
1193    packet_is_valid = TRUE;
1194
1195 done:
1196    return (packet_is_valid);
1197}
1198
1199static CFStringRef
1200eapmschapv2_copy_packet_description(const EAPPacketRef pkt,
1201				    bool * packet_is_valid)
1202{
1203    int				data_length;
1204    EAPMSCHAPv2PacketRef	ms_pkt_p = (EAPMSCHAPv2PacketRef)pkt;
1205    u_int16_t			length = EAPPacketGetLength(pkt);
1206    CFMutableStringRef		str = NULL;
1207    bool			valid;
1208
1209    valid = FALSE;
1210    switch (pkt->code) {
1211    case kEAPCodeRequest:
1212	break;
1213    case kEAPCodeResponse:
1214	break;
1215    default:
1216	/* don't handle it */
1217	goto done;
1218    }
1219    str = CFStringCreateMutable(NULL, 0);
1220    if (length < sizeof(EAPMSCHAPv2SuccessResponsePacket)) {
1221	STRING_APPEND(str, "invalid packet: length %d < min length %ld\n",
1222		      length, sizeof(*ms_pkt_p));
1223	goto done;
1224    }
1225    data_length = length - sizeof(EAPMSCHAPv2SuccessResponsePacket);
1226    STRING_APPEND(str, "EAP-MSCHAPv2 %s: Identifier %d Length %d OpCode %s ",
1227		  EAPCodeStr(pkt->code), pkt->identifier, length,
1228		  MSCHAPv2OpCodeStr(ms_pkt_p->op_code));
1229    /* Request */
1230    switch (ms_pkt_p->op_code) {
1231    case kMSCHAPv2OpCodeChallenge:
1232	valid = eapmschapv2_challenge_append(str, pkt, length);
1233	if (pkt->code == kEAPCodeResponse) {
1234	    valid = FALSE;
1235	    STRING_APPEND(str, "EAP Response contains "
1236			  "MSCHAPv2 Challenge (invalid)\n");
1237	}
1238	break;
1239    case kMSCHAPv2OpCodeResponse:
1240	valid = eapmschapv2_response_append(str, pkt, length);
1241	if (pkt->code == kEAPCodeRequest) {
1242	    valid = FALSE;
1243	    STRING_APPEND(str, "EAP Request contains "
1244			  "MSCHAPv2 Response (invalid)\n");
1245	}
1246	break;
1247    case kMSCHAPv2OpCodeSuccess:
1248	if (pkt->code == kEAPCodeRequest) {
1249	    valid = eapmschapv2_success_request_append(str, pkt, length);
1250	}
1251	break;
1252    case kMSCHAPv2OpCodeFailure:
1253	if (pkt->code == kEAPCodeRequest) {
1254	    valid = eapmschapv2_failure_request_append(str, pkt, length);
1255	}
1256	break;
1257    case kMSCHAPv2OpCodeChangePassword:
1258	valid = eapmschapv2_change_password_append(str, pkt, length);
1259	break;
1260    default:
1261	STRING_APPEND(str, "Unknown code %d\n", ms_pkt_p->op_code);
1262	if (data_length > 0) {
1263	    print_data_cfstr(str, &ms_pkt_p->mschapv2_id,
1264			     data_length);
1265	}
1266	break;
1267    }
1268
1269 done:
1270    *packet_is_valid = valid;
1271    return (str);
1272}
1273
1274static EAPType
1275eapmschapv2_type()
1276{
1277    return (kEAPTypeMSCHAPv2);
1278
1279}
1280
1281static const char *
1282eapmschapv2_name()
1283{
1284    return ("MSCHAPv2");
1285
1286}
1287
1288static EAPClientPluginVersion
1289eapmschapv2_version()
1290{
1291    return (kEAPClientPluginVersion);
1292}
1293
1294static void *
1295eapmschapv2_session_key(EAPClientPluginDataRef plugin, int * key_length)
1296{
1297    EAPMSCHAPv2PluginDataRef	context;
1298
1299    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
1300    *key_length = 0;
1301    if (context->session_key_valid == FALSE) {
1302	return (NULL);
1303    }
1304    *key_length = sizeof(context->session_key);
1305    return (&context->session_key);
1306}
1307
1308static void *
1309eapmschapv2_server_key(EAPClientPluginDataRef plugin, int * key_length)
1310{
1311    EAPMSCHAPv2PluginDataRef	context;
1312
1313    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
1314    *key_length = 0;
1315    if (context->session_key_valid == FALSE) {
1316	return (NULL);
1317    }
1318    *key_length = sizeof(context->session_key);
1319    return (&context->session_key);
1320}
1321
1322static int
1323eapmschapv2_msk_copy_bytes(EAPClientPluginDataRef plugin,
1324			   void * msk, int msk_size)
1325{
1326    EAPMSCHAPv2PluginDataRef	context;
1327    int				ret_msk_size;
1328
1329    context = (EAPMSCHAPv2PluginDataRef)plugin->private;
1330    if (msk_size < kEAPMasterSessionKeyMinimumSize
1331	|| context->session_key_valid == FALSE) {
1332	ret_msk_size = 0;
1333    }
1334    else {
1335	void *	offset;
1336
1337	offset = msk;
1338
1339	/* MasterReceiveKey */
1340	bcopy(context->session_key.master_receive_key, offset,
1341	      sizeof(context->session_key.master_receive_key));
1342	offset += sizeof(context->session_key.master_receive_key);
1343
1344	/* MasterSendKey */
1345	bcopy(context->session_key.master_send_key, offset,
1346	      sizeof(context->session_key.master_send_key));
1347	offset += sizeof(context->session_key.master_send_key);
1348
1349	/* Zero-padding */
1350	bzero(offset,
1351	      kEAPMasterSessionKeyMinimumSize - sizeof(context->session_key));
1352
1353	ret_msk_size = kEAPMasterSessionKeyMinimumSize;
1354    }
1355    return (ret_msk_size);
1356}
1357
1358static struct func_table_ent {
1359    const char *		name;
1360    void *			func;
1361} func_table[] = {
1362#if 0
1363    { kEAPClientPluginFuncNameIntrospect, eapmschapv2_introspect },
1364#endif /* 0 */
1365    { kEAPClientPluginFuncNameVersion, eapmschapv2_version },
1366    { kEAPClientPluginFuncNameEAPType, eapmschapv2_type },
1367    { kEAPClientPluginFuncNameEAPName, eapmschapv2_name },
1368    { kEAPClientPluginFuncNameInit, eapmschapv2_init },
1369    { kEAPClientPluginFuncNameFree, eapmschapv2_free },
1370    { kEAPClientPluginFuncNameProcess, eapmschapv2_process },
1371    { kEAPClientPluginFuncNameFreePacket, eapmschapv2_free_packet },
1372    { kEAPClientPluginFuncNameSessionKey, eapmschapv2_session_key },
1373    { kEAPClientPluginFuncNameServerKey, eapmschapv2_server_key },
1374    { kEAPClientPluginFuncNameMasterSessionKeyCopyBytes,
1375      eapmschapv2_msk_copy_bytes },
1376    { kEAPClientPluginFuncNameRequireProperties, eapmschapv2_require_props },
1377    { kEAPClientPluginFuncNamePublishProperties, eapmschapv2_publish_props },
1378    { kEAPClientPluginFuncNameCopyPacketDescription,
1379      eapmschapv2_copy_packet_description },
1380    { NULL, NULL},
1381};
1382
1383
1384EAPClientPluginFuncRef
1385eapmschapv2_introspect(EAPClientPluginFuncName name)
1386{
1387    struct func_table_ent * scan;
1388
1389
1390    for (scan = func_table; scan->name != NULL; scan++) {
1391	if (strcmp(name, scan->name) == 0) {
1392	    return (scan->func);
1393	}
1394    }
1395    return (NULL);
1396}
1397