File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / chap.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:39:23 2021 UTC (3 years, 3 months ago) by misho
Branches: mpd, MAIN
CVS tags: v5_9p16, v5_9, HEAD
mpd 5.9


/*
 * chap.c
 *
 * Written by Toshiharu OHNO <tony-o@iij.ad.jp>
 * Copyright (c) 1993, Internet Initiative Japan, Inc. All rights reserved.
 * See ``COPYRIGHT.iij''
 * 
 * Rewritten by Archie Cobbs <archie@freebsd.org>
 * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
 * See ``COPYRIGHT.whistle''
 */

#include "ppp.h"
#include "auth.h"
#include "msoft.h"
#include "util.h"
#include <openssl/md5.h>

/*
 * INTERNAL FUNCTIONS
 */

  static int	ChapHash(Link l, int alg, u_char *hash_value, u_char id,
		  const char *username, const char *secret,
		  const u_char *challenge, int clen, int local);
  static int	ChapHashAgree(int alg, const u_char *self, int slen,
		  const u_char *peer, int plen);
  static int	ChapParsePkt(const char *pref, const u_char *pkt, const int pkt_len,
		  char *peer_name, u_char *chap_value,
		  int *chap_value_size);
  static char	*ChapGetSecret(Link l, int alg, char *password);
  static void	ChapGenRandom(Link l, u_char *buf, int len);

/*
 * INTERNAL VARIABLES
 */

  static const u_char	gIdBytes[] = { 0x3b, 0x1e, 0x68 };

/*
 * ChapStart()
 */

void
ChapStart(Link l, int which)
{
  Auth 		a = &l->lcp.auth;
  ChapInfo 	chap = &l->lcp.auth.chap;
  ChapParams	cp = &l->lcp.auth.params.chap;
  
  chap->proto = PROTO_CHAP;

  if (l->originate == LINK_ORIGINATE_LOCAL)
    a->params.msoft.chap_alg = a->self_to_peer_alg;
  else
    a->params.msoft.chap_alg = a->peer_to_self_alg;
  
  switch (which)
  {
    case AUTH_SELF_TO_PEER:	/* Just wait for peer's challenge */
      break;

    case AUTH_PEER_TO_SELF:

      /* Invalidate any old challenge data */
      cp->chal_len = 0;

      /* Initialize retry counter and timer */
      chap->next_id = 1;
      chap->retry = AUTH_RETRIES;

      TimerInit(&chap->chalTimer, "ChalTimer",
	l->conf.retry_timeout * SECONDS, ChapChalTimeout, l);
      TimerStart(&chap->chalTimer);

      /* Send first challenge */
      ChapSendChallenge(l);
      break;

    default:
      assert(0);
  }
}

/*
 * ChapStop()
 */

void
ChapStop(ChapInfo chap)
{
  TimerStop(&chap->chalTimer);
  TimerStop(&chap->respTimer);
  if (chap->resp) {
    Freee(chap->resp);
    chap->resp = NULL;
  }
}

/*
 * ChapSendChallenge()
 */

void
ChapSendChallenge(Link l)
{
  Auth          const a = &l->lcp.auth;
  ChapInfo	chap = &a->chap;
  ChapParams	cp = &a->params.chap;
  u_char	*pkt;

  /* don't generate new challenges on re-transmit */
  if (cp->chal_len)
    goto send_pkt;

  /* Put random challenge data in buffer (only once for Microsoft CHAP) */
  switch (a->peer_to_self_alg) {
    case CHAP_ALG_MSOFT: {
	cp->chal_len = CHAP_MSOFT_CHAL_LEN;
	ChapGenRandom(l, cp->chal_data, cp->chal_len);
	if (l->originate == LINK_ORIGINATE_REMOTE) {
	  memcpy(a->params.msoft.msChal, cp->chal_data, cp->chal_len);
	}
      }
      break;
    case CHAP_ALG_MSOFTv2:
      cp->chal_len = CHAP_MSOFTv2_CHAL_LEN;
      ChapGenRandom(l, cp->chal_data, cp->chal_len);
      if (l->originate == LINK_ORIGINATE_REMOTE) {
	memcpy(a->params.msoft.msChal, cp->chal_data, cp->chal_len);
      }
      break;
    case CHAP_ALG_MD5:
      cp->chal_len = random() % 32 + 16;
      ChapGenRandom(l, cp->chal_data, cp->chal_len);
      break;
    default:
      assert(0);
  }
  assert(cp->chal_len <= (int)sizeof(cp->chal_data));

send_pkt:
  /* Build a challenge packet */
  pkt = Malloc(MB_AUTH, 1 + cp->chal_len + strlen(a->conf.authname) + 1);
  pkt[0] = cp->chal_len;
  memcpy(pkt + 1, cp->chal_data, cp->chal_len);
  memcpy(pkt + 1 + cp->chal_len,
    a->conf.authname, strlen(a->conf.authname));

  /* Send it off */
  AuthOutput(l, chap->proto,
    chap->proto == PROTO_CHAP ? CHAP_CHALLENGE : EAP_REQUEST,
    chap->next_id++, pkt,
    1 + cp->chal_len + strlen(a->conf.authname), 0,
    EAP_TYPE_MD5CHAL);
  Freee(pkt);
}

/*
 * ChapSendResponse()
 */

static void
ChapSendResponse(Link l)
{
  ChapInfo	chap = &l->lcp.auth.chap;

  /* Stop response timer */
  TimerStop(&chap->respTimer);

  /* Send response (possibly again) */
  assert(chap->resp);
  AuthOutput(l, chap->proto,
    chap->proto == PROTO_CHAP ? CHAP_RESPONSE : EAP_RESPONSE,
    chap->resp_id, chap->resp, chap->resp_len, 0, EAP_TYPE_MD5CHAL);

  /* Start re-send timer (only during authenticate phase where the
     authentication timer is still running) */
  if (l->lcp.phase == PHASE_AUTHENTICATE) {
    TimerInit(&chap->respTimer, "RespTimer",
      l->conf.retry_timeout * SECONDS,
      (void (*)(void *)) ChapSendResponse, (void *) l);
    TimerStart(&chap->respTimer);
  }
}

/*
 * ChapParsePkt()
 *
 */

static int
ChapParsePkt(const char *pref, const u_char *pkt, const int pkt_len,
  char *peer_name, u_char *chap_value, int *chap_value_size)
{
  int		val_len, name_len;

  /* Compute and check lengths */
  if (pkt == NULL
      || pkt_len < 1
      || (val_len = pkt[0]) < 1
      || val_len > CHAP_MAX_VAL
      || (name_len = (pkt_len - val_len - 1)) < 0
      || name_len > CHAP_MAX_NAME) {
    Log(LG_AUTH, ("[%s]   Bogus packet", pref));
    return(-1);
  }

  /* Extract stuff */
  memcpy(peer_name, pkt + 1 + val_len, name_len);
  peer_name[name_len] = 0;
  memcpy(chap_value, pkt + 1, val_len);
  *chap_value_size = val_len;
  Log(LG_AUTH, ("[%s]   Name: \"%s\"", pref, peer_name));
#if 0
  Log(LG_AUTH, ("[%s]   Value: %d bytes", pref, *chap_value_size));
#endif
  return(0);
}

/*
 * ChapChalTimeout()
 *
 * Timer expired for reply to challenge packet
 */

void
ChapChalTimeout(void *ptr)
{
  Link		const l = (Link) ptr;
  ChapInfo	const chap = &l->lcp.auth.chap;

  if (--chap->retry > 0) {
    TimerStart(&chap->chalTimer);
    ChapSendChallenge(l);
  }
}

/*
 * ChapInput()
 */

void
ChapInput(Link l, AuthData auth, const u_char *pkt, u_short len)
{
  Auth		const a = &l->lcp.auth;
  ChapInfo	const chap = &a->chap;
  char		peer_name[CHAP_MAX_NAME + 1];
  char		password[AUTH_MAX_PASSWORD];
  u_char	hash_value[CHAP_MAX_VAL];
  int		hash_value_size;
  
  chap->proto = auth->proto;
  
  switch (auth->code) {
    case CHAP_CHALLENGE:
      {
	u_char	value[CHAP_MAX_VAL];		/* Chap packet */
	int	value_len;			/* Packet length */
	char	*name, *secret;
	int	name_len, idFail;

	auth->alg = a->self_to_peer_alg;

	/* Check packet */
	if ((a->self_to_peer != PROTO_CHAP && a->self_to_peer != PROTO_EAP)
	    || l->lcp.phase != PHASE_AUTHENTICATE)
	  Log(LG_AUTH, ("[%s] CHAP: Not expected, but that's OK", l->name));
	if (ChapParsePkt(l->name, pkt, len, peer_name, value, &value_len) < 0)
	  break;

	/* Never respond to our own outstanding challenge */
	if (value_len == auth->params.chap.chal_len
	    && !memcmp(value, auth->params.chap.chal_data, auth->params.chap.chal_len)) {
	  Log(LG_AUTH, ("[%s] CHAP: SECURITY: peer sent same challenge! Ignoring.", l->name));
	  break;
	}

	/* Don't respond to a challenge that looks like it came from
	   us and has the wrong origination value embedded in it. This
	   avoids a security hole associated with using the same CHAP
	   password to authenticate in both directions on a link. */
	idFail = 0;
	do {
	  char	buf[sizeof(gIdBytes)];
	  int	chalOrig;

	  /* Check challenge length */
	  if (value_len < (int)sizeof(buf))
	    break;

	  /* Copy challenge bits and extract origination value */
	  memcpy(buf, value, sizeof(buf));
	  chalOrig = (buf[0] >> 6) & 0x03;
	  buf[0] &= 0x3f;

	  /* Check for same ID bytes in the challenge */
	  if (memcmp(buf, gIdBytes, sizeof(gIdBytes)) != 0)
	    break;

	  /* ID bytes match; origination value must be opposite. Note this
	     assumes that if we can tell the origination direction of a link,
	     then so can the peer. */
	  switch (l->originate) {
	    case LINK_ORIGINATE_LOCAL:
	      idFail = (chalOrig != LINK_ORIGINATE_REMOTE);
	      break;
	    case LINK_ORIGINATE_REMOTE:
	      idFail = (chalOrig != LINK_ORIGINATE_LOCAL);
	      break;
	    case LINK_ORIGINATE_UNKNOWN:
	    default:
	      idFail = 0;	/* XXX assumes leased line, etc is secure */
	      break;
	  }

	  /* Log failure */
	  if (idFail) {
	    Log(LG_AUTH,
	      ("[%s] CHAP: SECURITY: origination value check failed (%s,%s). Ignoring.",
	        l->name,
		LINK_ORIGINATION(l->originate),
		LINK_ORIGINATION(chalOrig)));
	    break;
	  }
	} while (0);
	if (idFail)
	  break;

	/*
	 * Name we use to authenticate ourselves:
	 *
	 * 1. The manually configured authname ("set authname ...")
	 * 2. The peer's supplied name
	 */
	if (*auth->conf.authname)
	  name = auth->conf.authname;
	else
	  name = peer_name;
	name_len = strlen(name);
	Log(LG_AUTH, ("[%s] CHAP: Using authname \"%s\"", l->name, name));

	strlcpy(auth->params.authname, name, sizeof(auth->params.authname));

	/* Get the corresponding secret */
	if ((strcmp(auth->conf.authname, auth->params.authname) == 0) && 
	    auth->conf.password[0] != 0) {
		strlcpy(password, auth->conf.password, sizeof(password));
	} else if (AuthGetData(auth->params.authname, password, 
	    sizeof(password), NULL, NULL) < 0) {
		Log(LG_AUTH, ("[%s] CHAP: Warning: no secret for \"%s\" found",
		    l->name, 
		    auth->params.authname));
		break;
	}

	secret = ChapGetSecret(l, a->self_to_peer_alg, password);

	/* Get hash value */
	if ((hash_value_size = ChapHash(l, a->self_to_peer_alg, hash_value,
	    auth->id, name, secret, value, value_len, 1)) < 0) {
	  Log(LG_AUTH, ("[%s] CHAP: Hash failure", l->name));
	  break;
	}

	/* Need to remember MS-CHAP stuff for use with MPPE encryption */
	switch (a->self_to_peer_alg) {
	case CHAP_ALG_MSOFT:
  	  if (l->originate == LINK_ORIGINATE_LOCAL)
		memcpy(a->params.msoft.msChal, value, CHAP_MSOFT_CHAL_LEN);
	  break;
	case CHAP_ALG_MSOFTv2:
  	  if (l->originate == LINK_ORIGINATE_LOCAL) {
		memcpy(a->params.msoft.msChal, value, CHAP_MSOFTv2_CHAL_LEN);
		memcpy(a->params.msoft.ntResp,
	    	    hash_value + offsetof(struct mschapv2value, ntHash),
		    CHAP_MSOFTv2_RESP_LEN);
	  }
	  break;
	}

	/* Build response packet */
	if (chap->resp)
	  Freee(chap->resp);
	chap->resp = Malloc(MB_AUTH, 1 + hash_value_size + name_len);
	chap->resp[0] = hash_value_size;
	memcpy(&chap->resp[1], hash_value, hash_value_size);
	memcpy(&chap->resp[1 + hash_value_size], name, name_len);
	chap->resp_len = 1 + hash_value_size + name_len;
	chap->resp_id = auth->id;

	/* Send response to peer */
	ChapSendResponse(l);
      }
      break;

    case CHAP_RESPONSE:
	auth->alg = a->peer_to_self_alg;

	/* Stop challenge timer */
	TimerStop(&chap->chalTimer);

	/* Check response */
	if ((a->peer_to_self != PROTO_CHAP && a->peer_to_self != PROTO_EAP)
	    || l->lcp.phase != PHASE_AUTHENTICATE)
	  Log(LG_AUTH, ("[%s] CHAP: Not expected, but that's OK", l->name));
	if (ChapParsePkt(l->name, pkt, len,
	    peer_name, auth->params.chap.value, &auth->params.chap.value_len) < 0) {
	  auth->why_fail = AUTH_FAIL_INVALID_PACKET;
	  ChapInputFinish(l, auth);
	  return;
	}

	/* Strip MS domain if any */
	if (!Enabled(&l->conf.options, LINK_CONF_MSDOMAIN))
	  if (a->peer_to_self_alg == CHAP_ALG_MSOFT
	      || a->peer_to_self_alg == CHAP_ALG_MSOFTv2) {
	    char	*s;

	    if ((s = strrchr(peer_name, '\\')))
	      memmove(peer_name, s + 1, strlen(s) + 1);
	  }

	strlcpy(auth->params.authname, peer_name, sizeof(auth->params.authname));
	
	auth->finish = ChapInputFinish;
	AuthAsyncStart(l, auth);
	return;

    case CHAP_SUCCESS:
    case CHAP_FAILURE:

	auth->alg = a->self_to_peer_alg;

      /* Stop response timer */
      TimerStop(&chap->respTimer);
      if (chap->resp) {
	Freee(chap->resp);
	chap->resp = NULL;
      }

      /* Appropriate? */
      if (a->self_to_peer != PROTO_CHAP
	  || l->lcp.phase != PHASE_AUTHENTICATE) {
	Log(LG_AUTH, ("[%s] CHAP: Not expected, but that's OK", l->name));
	break;
      }

      /* Log message */
      ShowMesg(LG_AUTH, l->name, (const char*)pkt, len);
      AuthFinish(l, AUTH_SELF_TO_PEER, auth->code == CHAP_SUCCESS);
      break;
      
    case CHAP_MS_V1_CHANGE_PW:
      Log(LG_AUTH, ("[%s] CHAP: Sorry changing passwords using MS-CHAPv1 is not yet implemented",
	l->name));
      goto badResponse;

    case CHAP_MS_V2_CHANGE_PW:
      Log(LG_AUTH, ("[%s] CHAP: Sorry changing passwords using MS-CHAPv2 is not yet implemented",
	l->name));
      goto badResponse;

    default:
      Log(LG_AUTH, ("[%s] CHAP: unknown code %d", l->name, auth->code));
      break;
  }
    AuthDataDestroy(auth);
    return;
  
badResponse:
  {
    char	failMesg[64];

    auth->why_fail = AUTH_FAIL_NOT_EXPECTED;
    AuthFailMsg(auth, failMesg, sizeof(failMesg));
    AuthOutput(l, auth->proto, auth->proto == PROTO_CHAP ? CHAP_FAILURE : EAP_FAILURE,
	auth->id, (u_char *)failMesg, strlen(failMesg), 0, EAP_TYPE_MD5CHAL);
    AuthFinish(l, AUTH_PEER_TO_SELF, FALSE);
    AuthDataDestroy(auth);
  }
}

/*
 * ChapInputFinish()
 *
 * Possible return point from the asynch auth handler.
 * 
 */
 
void 
ChapInputFinish(Link l, AuthData auth)
{
    Auth	a = &l->lcp.auth;
    ChapInfo	chap = &a->chap;
    u_char	hash_value[CHAP_MAX_VAL];
    int		hash_value_size;
    char	ackMesg[128], *secret;
   
    Log(LG_AUTH, ("[%s] CHAP: Auth return status: %s", 
	l->name, AuthStatusText(auth->status)));
    
    if (auth->status == AUTH_STATUS_BUSY) {
	AuthDataDestroy(auth);  
	return;
    }
    
    if (a->peer_to_self_alg == CHAP_ALG_MSOFTv2 && 
      auth->mschapv2resp != NULL) {
	strlcpy(ackMesg, auth->mschapv2resp, sizeof(ackMesg));
    } else if (auth->reply_message != NULL) {
	strlcpy(ackMesg, auth->reply_message, sizeof(ackMesg));
    } else {
	strlcpy(ackMesg, AUTH_MSG_WELCOME, sizeof(ackMesg));
    }

    if (auth->status == AUTH_STATUS_FAIL)
	goto badResponse;
    else if (auth->status == AUTH_STATUS_SUCCESS)
	goto goodResponse;
  
    /* Copy in peer challenge for MS-CHAPv2 */
    if (a->peer_to_self_alg == CHAP_ALG_MSOFTv2)
	memcpy(hash_value, a->params.chap.value, 16);
    
    secret = ChapGetSecret(l, a->peer_to_self_alg, a->params.password);

    /* Get expected hash value */
    if ((hash_value_size = ChapHash(l, a->peer_to_self_alg, hash_value, auth->id,
      a->params.authname, secret, a->params.chap.chal_data, a->params.chap.chal_len,
      0)) < 0) {
	Log(LG_AUTH, ("[%s] CHAP: Hash failure", l->name));
	auth->why_fail = AUTH_FAIL_INVALID_PACKET;
	goto badResponse;
    }

    /* Compare with peer's response */
    if (a->params.chap.chal_len == 0
      || !ChapHashAgree(a->peer_to_self_alg, hash_value, hash_value_size,
		a->params.chap.value, a->params.chap.value_len)) {
	Log(LG_AUTH, ("[%s] CHAP: Invalid response", l->name));
	auth->why_fail = AUTH_FAIL_INVALID_LOGIN;
	goto badResponse;
    }
  
    /* Response is good */
    Log(LG_AUTH, ("[%s] CHAP: Response is valid", l->name));

    if (a->peer_to_self_alg == CHAP_ALG_MSOFTv2) {
	struct mschapv2value *const pv = (struct mschapv2value *)a->params.chap.value;
	char hex[41];
	u_char authresp[20];
	int i;

	/* Generate MS-CHAPv2 'authenticator response' */
	GenerateAuthenticatorResponse(a->params.msoft.nt_hash, pv->ntHash,
    	    pv->peerChal, a->params.chap.chal_data, a->params.authname, authresp);
	for (i = 0; i < 20; i++)
    	    sprintf(hex + (i * 2), "%02X", authresp[i]);
    	/* If we have reply message, send it. */
	if (auth->reply_message != NULL) {
	    snprintf(ackMesg, sizeof(ackMesg), "S=%s M=%s",
		hex, auth->reply_message);
	} else
	    snprintf(ackMesg, sizeof(ackMesg), "S=%s", hex);
    }
  
goodResponse:
#ifdef USE_OPIE
    /* make a dummy verify to force an update of the opiekeys database */
    if (a->params.authentic == AUTH_CONF_OPIE)
	opieverify(&auth->opie.data, a->params.password);
#endif

    /* Need to remember MS-CHAP stuff for use with MPPE encryption */
    if (l->originate == LINK_ORIGINATE_REMOTE &&
      a->peer_to_self_alg == CHAP_ALG_MSOFTv2 &&
      !memcmp(a->params.msoft.ntResp, gMsoftZeros, CHAP_MSOFTv2_RESP_LEN)) {
	memcpy(a->params.msoft.ntResp,
    	    a->params.chap.value + offsetof(struct mschapv2value, ntHash),
    	    CHAP_MSOFTv2_RESP_LEN);
    }
  
    Log(LG_AUTH, ("[%s] CHAP: Reply message: %s", l->name, ackMesg));
    AuthOutput(l, chap->proto, chap->proto == PROTO_CHAP ? CHAP_SUCCESS : EAP_SUCCESS,
	auth->id, (u_char *)ackMesg, strlen(ackMesg), 0, EAP_TYPE_MD5CHAL);
    AuthFinish(l, AUTH_PEER_TO_SELF, TRUE);
    AuthDataDestroy(auth);
    return;  

badResponse:
  {
    char        failMesg[64];

    AuthFailMsg(auth, failMesg, sizeof(failMesg));
    Log(LG_AUTH, ("[%s] CHAP: Reply message: %s", l->name, failMesg));
    AuthOutput(l, chap->proto, chap->proto == PROTO_CHAP ? CHAP_FAILURE : EAP_FAILURE,
	auth->id, (u_char *)failMesg, strlen(failMesg), 0, EAP_TYPE_MD5CHAL);
    AuthFinish(l, AUTH_PEER_TO_SELF, FALSE);
    AuthDataDestroy(auth);  
  }
}

/*
 * ChapGetSecret()
 * 
 * returns either the plaintext pass for CHAP-MD5
 * or the NT-Hash for MS-CHAP. Set's credentials for
 * MPPE-Key derivation
 */

static char *
ChapGetSecret(Link l, int alg, char *password)
{
  Auth		a = &l->lcp.auth;
  char		*pw;
  
  if (alg == CHAP_ALG_MD5)
    pw = password;
  else {
    if (!a->params.msoft.has_nt_hash)
    {
      NTPasswordHash(password, a->params.msoft.nt_hash);
      NTPasswordHashHash(a->params.msoft.nt_hash, a->params.msoft.nt_hash_hash);
      LMPasswordHash(password, a->params.msoft.lm_hash);
      a->params.msoft.has_nt_hash = TRUE;
      a->params.msoft.has_lm_hash = TRUE;
    }

    pw = (char *) a->params.msoft.nt_hash;
  }

  return pw;
}

/*
 * ChapGenRandom()
 */

static void
ChapGenRandom(Link l, u_char *buf, int len)
{
  int	k;

  /* Prefix with our unique ID plus origination value */
  for (k = 0; k < (int)sizeof(gIdBytes) && k < len; k++)
    buf[k] = gIdBytes[k];
  buf[0] |= (l->originate & 0x03) << 6;

  /* Fill the rest with semi-random bytes */
  for (; k < len; k++)
    buf[k] = random() & 0xff;
}

/*
 * ChapHash()
 */

static int
ChapHash(Link l, int alg, u_char *hash_value, u_char id, const char *username,
	const char *secret, const u_char *challenge, int clen, int local)
{
  int	hash_size;

  switch (alg) {
    case CHAP_ALG_MD5:
      {
	MD5_CTX	md5ctx;

	MD5_Init(&md5ctx);
	MD5_Update(&md5ctx, &id, 1);
	MD5_Update(&md5ctx, secret, strlen(secret));
	MD5_Update(&md5ctx, challenge, clen);
	MD5_Final(hash_value, &md5ctx);
	hash_size = 16;
      }
      break;

    case CHAP_ALG_MSOFT:
      {
	struct mschapvalue	*const val = (struct mschapvalue *) hash_value;

	/* We don't generate the LANManager hash because it's too insecure */
	memset(val->lmHash, 0, sizeof(val->lmHash));
	NTChallengeResponse(challenge, secret, val->ntHash);
	val->useNT = 1;
	hash_size = 49;
      }
      break;
    case CHAP_ALG_MSOFTv2:
      {
	struct mschapv2value *const val = (struct mschapv2value *) hash_value;
	const char *s;

	if ((s = strrchr(username, '\\')) != NULL)
	  username = s + 1;
	if (local) {			/* generate reverse 'peer challenge' */
	  ChapGenRandom(l, val->peerChal, sizeof(val->peerChal));
	  memset(val->reserved, 0, sizeof(val->reserved));
	  val->flags = 0; /* rfc2759, paragraph 4 says that flags MUST be zero */
	}
	GenerateNTResponse(challenge,
	  val->peerChal, username, secret, val->ntHash);

	hash_size = 49;
      }
      break;

    default:
      return(-1);
  }

  /* Done */
  return(hash_size);
}

/*
 * ChapHashAgree()
 */

static int
ChapHashAgree(int alg, const u_char *self, int slen,
	const u_char *peer, int plen)
{
  switch (alg)
  {
    case CHAP_ALG_MD5:
      return(slen == plen && !memcmp(self, peer, slen));

    case CHAP_ALG_MSOFT:
      {
	const struct mschapvalue *const sv = (const struct mschapvalue *) self;
	const struct mschapvalue *const pv = (const struct mschapvalue *) peer;

	if (slen != 49 || plen != 49)
	  return(0);
	if (sv->useNT != 1 || pv->useNT != 1)
	  return(0);
	return(!memcmp(&sv->ntHash, &pv->ntHash, sizeof(sv->ntHash)));
      }
    case CHAP_ALG_MSOFTv2:
      {
	const struct mschapv2value *const sv =(const struct mschapv2value *) self;
	const struct mschapv2value *const pv =(const struct mschapv2value *) peer;

	if (slen != 49 || plen != 49)
	  return(0);
	return(!memcmp(&sv->ntHash, &pv->ntHash, sizeof(sv->ntHash)));
      }

    default:
      return(0);
  }
}

/*
 * ChapCode()
 */

const char *
ChapCode(int code, char *buf, size_t len)
{
  switch (code) {
    case CHAP_CHALLENGE:
	strlcpy(buf, "CHALLENGE", len);
	break;
    case CHAP_RESPONSE:
	strlcpy(buf, "RESPONSE", len);
	break;
    case CHAP_SUCCESS:
	strlcpy(buf, "SUCCESS", len);
	break;
    case CHAP_FAILURE:
	strlcpy(buf, "FAILURE", len);
	break;
    default:
	snprintf(buf, len, "code%d", code);
  }
  return(buf);
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>