PPP, CHAP, reworked to pass ppp_pcb pointer to CHAP childs (MD5, MSCHAP, MSCHAPv2)

We are going to need ppp_pcb* in MSCHAP and MSCHAPv2 for MPPE for
int mppe_keys_set, u_char mppe_send_key and u_char mppe_recv_key
which are currently global variable which must be moved to ppp_pcb.
This commit is contained in:
Sylvain Rochet 2015-04-18 17:29:55 +02:00
parent c51ed84fd8
commit ab46ac9bd8
5 changed files with 55 additions and 46 deletions

View File

@ -132,17 +132,17 @@ struct chap_digest_type {
* Note: challenge and response arguments below are formatted as * Note: challenge and response arguments below are formatted as
* a length byte followed by the actual challenge/response data. * a length byte followed by the actual challenge/response data.
*/ */
void (*generate_challenge)(unsigned char *challenge); void (*generate_challenge)(ppp_pcb *pcb, unsigned char *challenge);
int (*verify_response)(int id, const char *name, int (*verify_response)(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len, const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response, const unsigned char *challenge, const unsigned char *response,
char *message, int message_space); char *message, int message_space);
#endif /* PPP_SERVER */ #endif /* PPP_SERVER */
void (*make_response)(unsigned char *response, int id, const char *our_name, void (*make_response)(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len, const unsigned char *challenge, const char *secret, int secret_len,
const unsigned char *priv); const unsigned char *priv);
int (*check_success)(unsigned char *pkt, int len, unsigned char *priv); int (*check_success)(ppp_pcb *pcb, unsigned char *pkt, int len, unsigned char *priv);
void (*handle_failure)(unsigned char *pkt, int len); void (*handle_failure)(ppp_pcb *pcb, unsigned char *pkt, int len);
}; };
/* /*

View File

@ -92,15 +92,6 @@ extern void set_mppe_enc_types(int, int);
#define MS_CHAP2_AUTHENTICATEE 0 #define MS_CHAP2_AUTHENTICATEE 0
#define MS_CHAP2_AUTHENTICATOR 1 #define MS_CHAP2_AUTHENTICATOR 1
void ChapMS (u_char *, char *, int, u_char *);
void ChapMS2 (u_char *, u_char *, char *, char *, int,
u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
#if MPPE_SUPPORT
void mppe_set_keys (u_char *, u_char[MD4_SIGNATURE_SIZE]);
void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
u_char NTResponse[24], int IsServer);
#endif /* MPPE_SUPPORT */
void ChallengeHash (u_char[16], u_char *, char *, u_char[8]); void ChallengeHash (u_char[16], u_char *, char *, u_char[8]);
void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],

View File

@ -54,15 +54,16 @@
#define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE 3 /* 2^3-1 = 7, 17+7 = 24 */ #define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE 3 /* 2^3-1 = 7, 17+7 = 24 */
#if PPP_SERVER #if PPP_SERVER
static void chap_md5_generate_challenge(unsigned char *cp) { static void chap_md5_generate_challenge(ppp_pcb *pcb, unsigned char *cp) {
int clen; int clen;
LWIP_UNUSED_ARG(pcb);
clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE); clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE);
*cp++ = clen; *cp++ = clen;
random_bytes(cp, clen); random_bytes(cp, clen);
} }
static int chap_md5_verify_response(int id, const char *name, static int chap_md5_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len, const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response, const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) { char *message, int message_space) {
@ -71,6 +72,7 @@ static int chap_md5_verify_response(int id, const char *name,
unsigned char hash[MD5_HASH_SIZE]; unsigned char hash[MD5_HASH_SIZE];
int challenge_len, response_len; int challenge_len, response_len;
LWIP_UNUSED_ARG(name); LWIP_UNUSED_ARG(name);
LWIP_UNUSED_ARG(pcb);
challenge_len = *challenge++; challenge_len = *challenge++;
response_len = *response++; response_len = *response++;
@ -93,7 +95,7 @@ static int chap_md5_verify_response(int id, const char *name,
} }
#endif /* PPP_SERVER */ #endif /* PPP_SERVER */
static void chap_md5_make_response(unsigned char *response, int id, const char *our_name, static void chap_md5_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len, const unsigned char *challenge, const char *secret, int secret_len,
const unsigned char *private_) { const unsigned char *private_) {
md5_context ctx; md5_context ctx;
@ -101,6 +103,7 @@ static void chap_md5_make_response(unsigned char *response, int id, const char *
int challenge_len = *challenge++; int challenge_len = *challenge++;
LWIP_UNUSED_ARG(our_name); LWIP_UNUSED_ARG(our_name);
LWIP_UNUSED_ARG(private_); LWIP_UNUSED_ARG(private_);
LWIP_UNUSED_ARG(pcb);
md5_starts(&ctx); md5_starts(&ctx);
md5_update(&ctx, &idbyte, 1); md5_update(&ctx, &idbyte, 1);

View File

@ -258,7 +258,7 @@ static void chap_generate_challenge(ppp_pcb *pcb) {
p = pcb->chap_server.challenge; p = pcb->chap_server.challenge;
MAKEHEADER(p, PPP_CHAP); MAKEHEADER(p, PPP_CHAP);
p += CHAP_HDRLEN; p += CHAP_HDRLEN;
pcb->chap_server.digest->generate_challenge(p); pcb->chap_server.digest->generate_challenge(pcb, p);
clen = *p; clen = *p;
nlen = strlen(pcb->chap_server.name); nlen = strlen(pcb->chap_server.name);
memcpy(p + 1 + clen, pcb->chap_server.name, nlen); memcpy(p + 1 + clen, pcb->chap_server.name, nlen);
@ -418,7 +418,7 @@ static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourn
ppp_error("No CHAP secret found for authenticating %q", name); ppp_error("No CHAP secret found for authenticating %q", name);
return 0; return 0;
} }
ok = digest->verify_response(id, name, secret, secret_len, challenge, ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge,
response, message, message_space); response, message, message_space);
memset(secret, 0, sizeof(secret)); memset(secret, 0, sizeof(secret));
@ -472,7 +472,7 @@ static void chap_respond(ppp_pcb *pcb, int id,
MAKEHEADER(outp, PPP_CHAP); MAKEHEADER(outp, PPP_CHAP);
outp += CHAP_HDRLEN; outp += CHAP_HDRLEN;
pcb->chap_client.digest->make_response(outp, id, pcb->chap_client.name, pkt, pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt,
secret, secret_len, pcb->chap_client.priv); secret, secret_len, pcb->chap_client.priv);
memset(secret, 0, secret_len); memset(secret, 0, secret_len);
@ -504,13 +504,13 @@ static void chap_handle_status(ppp_pcb *pcb, int code, int id,
if (code == CHAP_SUCCESS) { if (code == CHAP_SUCCESS) {
/* used for MS-CHAP v2 mutual auth, yuck */ /* used for MS-CHAP v2 mutual auth, yuck */
if (pcb->chap_client.digest->check_success != NULL) { if (pcb->chap_client.digest->check_success != NULL) {
if (!(*pcb->chap_client.digest->check_success)(pkt, len, pcb->chap_client.priv)) if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv))
code = CHAP_FAILURE; code = CHAP_FAILURE;
} else } else
msg = "CHAP authentication succeeded"; msg = "CHAP authentication succeeded";
} else { } else {
if (pcb->chap_client.digest->handle_failure != NULL) if (pcb->chap_client.digest->handle_failure != NULL)
(*pcb->chap_client.digest->handle_failure)(pkt, len); (*pcb->chap_client.digest->handle_failure)(pcb, pkt, len);
else else
msg = "CHAP authentication failed"; msg = "CHAP authentication failed";
} }

View File

@ -128,10 +128,18 @@ static void ChapMS_LANMan (u_char *, char *, int, u_char *);
#endif #endif
#if MPPE_SUPPORT #if MPPE_SUPPORT
static void Set_Start_Key (u_char *, char *, int); static void mppe_set_keys(ppp_pcb *pcb, u_char *rchallenge,
static void SetMasterKeys (char *, int, u_char[24], int); u_char PasswordHashHash[MD4_SIGNATURE_SIZE]);
static void Set_Start_Key (ppp_pcb *pcb, u_char *, char *, int);
static void mppe_set_keys2(ppp_pcb *pcb, u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
u_char NTResponse[24], int IsServer);
static void SetMasterKeys (ppp_pcb *pcb, char *, int, u_char[24], int);
#endif /* MPPE_SUPPORT */ #endif /* MPPE_SUPPORT */
static void ChapMS (ppp_pcb *pcb, u_char *, char *, int, u_char *);
static void ChapMS2 (ppp_pcb *pcb, u_char *, u_char *, char *, char *, int,
u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
#ifdef MSLANMAN #ifdef MSLANMAN
bool ms_lanman = 0; /* Use LanMan password instead of NT */ bool ms_lanman = 0; /* Use LanMan password instead of NT */
/* Has meaning only with MS-CHAP challenges */ /* Has meaning only with MS-CHAP challenges */
@ -180,7 +188,9 @@ static option_t chapms_option_list[] = {
* The length goes in challenge[0] and the actual challenge starts * The length goes in challenge[0] and the actual challenge starts
* at challenge[1]. * at challenge[1].
*/ */
static void chapms_generate_challenge(unsigned char *challenge) { static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
LWIP_UNUSED_ARG(pcb);
*challenge++ = 8; *challenge++ = 8;
#ifdef DEBUGMPPEKEY #ifdef DEBUGMPPEKEY
if (mschap_challenge && strlen(mschap_challenge) == 8) if (mschap_challenge && strlen(mschap_challenge) == 8)
@ -190,7 +200,9 @@ static void chapms_generate_challenge(unsigned char *challenge) {
random_bytes(challenge, 8); random_bytes(challenge, 8);
} }
static void chapms2_generate_challenge(unsigned char *challenge) { static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
LWIP_UNUSED_ARG(pcb);
*challenge++ = 16; *challenge++ = 16;
#ifdef DEBUGMPPEKEY #ifdef DEBUGMPPEKEY
if (mschap_challenge && strlen(mschap_challenge) == 16) if (mschap_challenge && strlen(mschap_challenge) == 16)
@ -200,7 +212,7 @@ static void chapms2_generate_challenge(unsigned char *challenge) {
random_bytes(challenge, 16); random_bytes(challenge, 16);
} }
static int chapms_verify_response(int id, const char *name, static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len, const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response, const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) { char *message, int message_space) {
@ -224,7 +236,7 @@ static int chapms_verify_response(int id, const char *name,
#endif #endif
/* Generate the expected response. */ /* Generate the expected response. */
ChapMS((u_char *)challenge, (char *)secret, secret_len, md); ChapMS(pcb, (u_char *)challenge, (char *)secret, secret_len, md);
#ifdef MSLANMAN #ifdef MSLANMAN
/* Determine which part of response to verify against */ /* Determine which part of response to verify against */
@ -248,7 +260,7 @@ static int chapms_verify_response(int id, const char *name,
return 0; return 0;
} }
static int chapms2_verify_response(int id, const char *name, static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len, const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response, const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) { char *message, int message_space) {
@ -263,7 +275,7 @@ static int chapms2_verify_response(int id, const char *name,
goto bad; /* not even the right length */ goto bad; /* not even the right length */
/* Generate the expected response and our mutual auth. */ /* Generate the expected response and our mutual auth. */
ChapMS2((u_char*)challenge, (u_char*)&response[MS_CHAP2_PEER_CHALLENGE], (char*)name, ChapMS2(pcb, (u_char*)challenge, (u_char*)&response[MS_CHAP2_PEER_CHALLENGE], (char*)name,
(char *)secret, secret_len, md, (char *)secret, secret_len, md,
(unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
@ -325,7 +337,7 @@ static int chapms2_verify_response(int id, const char *name,
} }
#endif /* PPP_SERVER */ #endif /* PPP_SERVER */
static void chapms_make_response(unsigned char *response, int id, const char *our_name, static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len, const unsigned char *challenge, const char *secret, int secret_len,
const unsigned char *private_) { const unsigned char *private_) {
LWIP_UNUSED_ARG(id); LWIP_UNUSED_ARG(id);
@ -333,16 +345,16 @@ static void chapms_make_response(unsigned char *response, int id, const char *ou
LWIP_UNUSED_ARG(private_); LWIP_UNUSED_ARG(private_);
challenge++; /* skip length, should be 8 */ challenge++; /* skip length, should be 8 */
*response++ = MS_CHAP_RESPONSE_LEN; *response++ = MS_CHAP_RESPONSE_LEN;
ChapMS((u_char*)challenge, (char*)secret, secret_len, response); ChapMS(pcb, (u_char*)challenge, (char*)secret, secret_len, response);
} }
static void chapms2_make_response(unsigned char *response, int id, const char *our_name, static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len, const unsigned char *challenge, const char *secret, int secret_len,
const unsigned char *private_) { const unsigned char *private_) {
LWIP_UNUSED_ARG(id); LWIP_UNUSED_ARG(id);
challenge++; /* skip length, should be 16 */ challenge++; /* skip length, should be 16 */
*response++ = MS_CHAP2_RESPONSE_LEN; *response++ = MS_CHAP2_RESPONSE_LEN;
ChapMS2((u_char*)challenge, ChapMS2(pcb, (u_char*)challenge,
#ifdef DEBUGMPPEKEY #ifdef DEBUGMPPEKEY
mschap2_peer_challenge, mschap2_peer_challenge,
#else #else
@ -352,7 +364,9 @@ static void chapms2_make_response(unsigned char *response, int id, const char *o
MS_CHAP2_AUTHENTICATEE); MS_CHAP2_AUTHENTICATEE);
} }
static int chapms2_check_success(unsigned char *msg, int len, unsigned char *private_) { static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) {
LWIP_UNUSED_ARG(pcb);
if ((len < MS_AUTH_RESPONSE_LENGTH + 2) || if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
strncmp((char *)msg, "S=", 2) != 0) { strncmp((char *)msg, "S=", 2) != 0) {
/* Packet does not start with "S=" */ /* Packet does not start with "S=" */
@ -380,10 +394,11 @@ static int chapms2_check_success(unsigned char *msg, int len, unsigned char *pri
return 1; return 1;
} }
static void chapms_handle_failure(unsigned char *inp, int len) { static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) {
int err; int err;
const char *p; const char *p;
char msg[64]; char msg[64];
LWIP_UNUSED_ARG(pcb);
/* We want a null-terminated string for strxxx(). */ /* We want a null-terminated string for strxxx(). */
len = LWIP_MIN(len, 63); len = LWIP_MIN(len, 63);
@ -645,7 +660,7 @@ static void GenerateAuthenticatorResponsePlain
* Set mppe_xxxx_key from the NTPasswordHashHash. * Set mppe_xxxx_key from the NTPasswordHashHash.
* RFC 2548 (RADIUS support) requires us to export this function (ugh). * RFC 2548 (RADIUS support) requires us to export this function (ugh).
*/ */
void mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]) { static void mppe_set_keys(ppp_pcb *pcb, u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]) {
sha1_context sha1Context; sha1_context sha1Context;
u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
@ -665,7 +680,7 @@ void mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZ
/* /*
* Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079) * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
*/ */
static void Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) { static void Set_Start_Key(ppp_pcb *pcb, u_char *rchallenge, char *secret, int secret_len) {
u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
@ -675,7 +690,7 @@ static void Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) {
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
mppe_set_keys(rchallenge, PasswordHashHash); mppe_set_keys(pcb, rchallenge, PasswordHashHash);
} }
/* /*
@ -684,7 +699,7 @@ static void Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) {
* This helper function used in the Winbind module, which gets the * This helper function used in the Winbind module, which gets the
* NTHashHash from the server. * NTHashHash from the server.
*/ */
void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], static void mppe_set_keys2(ppp_pcb *pcb, u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
u_char NTResponse[24], int IsServer) { u_char NTResponse[24], int IsServer) {
sha1_context sha1Context; sha1_context sha1Context;
u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
@ -776,7 +791,7 @@ void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
/* /*
* Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
*/ */
static void SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer) { static void SetMasterKeys(ppp_pcb *pcb, char *secret, int secret_len, u_char NTResponse[24], int IsServer) {
u_char unicodePassword[MAX_NT_PASSWORD * 2]; u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE]; u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
@ -784,13 +799,13 @@ static void SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], i
ascii2unicode(secret, secret_len, unicodePassword); ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
mppe_set_keys2(PasswordHashHash, NTResponse, IsServer); mppe_set_keys2(pcb, PasswordHashHash, NTResponse, IsServer);
} }
#endif /* MPPE_SUPPORT */ #endif /* MPPE_SUPPORT */
void ChapMS(u_char *rchallenge, char *secret, int secret_len, static void ChapMS(ppp_pcb *pcb, u_char *rchallenge, char *secret, int secret_len,
unsigned char *response) { unsigned char *response) {
BZERO(response, MS_CHAP_RESPONSE_LEN); BZERO(response, MS_CHAP_RESPONSE_LEN);
@ -807,7 +822,7 @@ void ChapMS(u_char *rchallenge, char *secret, int secret_len,
#endif #endif
#if MPPE_SUPPORT #if MPPE_SUPPORT
Set_Start_Key(rchallenge, secret, secret_len); Set_Start_Key(pcb, rchallenge, secret, secret_len);
#endif /* MPPE_SUPPORT */ #endif /* MPPE_SUPPORT */
} }
@ -822,7 +837,7 @@ void ChapMS(u_char *rchallenge, char *secret, int secret_len,
* The PeerChallenge field of response is then used for calculation of the * The PeerChallenge field of response is then used for calculation of the
* Authenticator Response. * Authenticator Response.
*/ */
void ChapMS2(u_char *rchallenge, u_char *PeerChallenge, static void ChapMS2(ppp_pcb *pcb, u_char *rchallenge, u_char *PeerChallenge,
char *user, char *secret, int secret_len, unsigned char *response, char *user, char *secret, int secret_len, unsigned char *response,
u_char authResponse[], int authenticator) { u_char authResponse[], int authenticator) {
/* ARGSUSED */ /* ARGSUSED */
@ -851,7 +866,7 @@ void ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
rchallenge, user, authResponse); rchallenge, user, authResponse);
#if MPPE_SUPPORT #if MPPE_SUPPORT
SetMasterKeys(secret, secret_len, SetMasterKeys(pcb, secret, secret_len,
&response[MS_CHAP2_NTRESP], authenticator); &response[MS_CHAP2_NTRESP], authenticator);
#endif /* MPPE_SUPPORT */ #endif /* MPPE_SUPPORT */
} }