home *** CD-ROM | disk | FTP | other *** search
- /*
- * chap.c - Crytographic Handshake Authentication Protocol.
- *
- * Copyright (c) 1991 Gregory M. Christy.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Gregory M. Christy. The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- /*
- * TODO:
- */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <syslog.h>
-
- #ifdef STREAMS
- #include <sys/socket.h>
- #include <net/if.h>
- #include <sys/stream.h>
- #endif
-
- #include "ppp.h"
- #include "fsm.h"
- #include "lcp.h"
- #include "chap.h"
- #include "upap.h"
- #include "ipcp.h"
- #include "md5.h"
-
- chap_state chap[NPPP]; /* CHAP state; one for each unit */
-
- static void ChapTimeout __ARGS((caddr_t));
- static void ChapReceiveChallenge __ARGS((chap_state *, u_char *, u_char, int));
- static void ChapReceiveResponse __ARGS((chap_state *, u_char *, u_char, int));
- static void ChapReceiveSuccess __ARGS((chap_state *, u_char *, u_char, int));
- static void ChapReceiveFailure __ARGS((chap_state *, u_char *, u_char, int));
- static void ChapSendStatus __ARGS((chap_state *, u_char, u_char,
- u_char *, int));
- static void ChapSendChallenge __ARGS((chap_state *));
- static void ChapSendResponse __ARGS((chap_state *, u_char, u_char *, int));
- static void ChapGenChallenge __ARGS((u_char, u_char *));
-
- extern double drand48 __ARGS((void));
-
- /*
- * ChapInit - Initialize a CHAP unit.
- */
- void
- ChapInit(unit)
- int unit;
- {
- chap_state *cstate = &chap[unit];
-
- cstate->unit = unit;
- cstate->chal_str[0] = '\000';
- cstate->chal_len = 0;
- cstate->clientstate = CHAPCS_CLOSED;
- cstate->serverstate = CHAPSS_CLOSED;
- cstate->flags = 0;
- cstate->id = 0;
- cstate->timeouttime = CHAP_DEFTIMEOUT;
- cstate->retransmits = 0;
- srand48((long) time(NULL)); /* joggle random number generator */
- }
-
-
- /*
- * ChapAuthWithPeer - Authenticate us with our peer (start client).
- *
- */
- void
- ChapAuthWithPeer(unit)
- int unit;
- {
- chap_state *cstate = &chap[unit];
-
- cstate->flags &= ~CHAPF_AWPPENDING; /* Clear pending flag */
-
- /* Protect against programming errors that compromise security */
- if (cstate->serverstate != CHAPSS_CLOSED ||
- cstate->flags & CHAPF_APPENDING) {
- CHAPDEBUG((LOG_INFO,
- "ChapAuthWithPeer: we were called already!"))
- return;
- }
-
- if (cstate->clientstate == CHAPCS_CHALLENGE_SENT || /* should we be here? */
- cstate->clientstate == CHAPCS_OPEN)
- return;
-
- /* Lower layer up? */
- if (!(cstate->flags & CHAPF_LOWERUP)) {
- cstate->flags |= CHAPF_AWPPENDING; /* Nah, Wait */
- return;
- }
- ChapSendChallenge(cstate); /* crank it up dude! */
- TIMEOUT(ChapTimeout, (caddr_t) cstate, cstate->timeouttime);
- /* set-up timeout */
- cstate->clientstate = CHAPCS_CHALLENGE_SENT; /* update state */
- cstate->retransmits = 0;
- }
-
-
- /*
- * ChapAuthPeer - Authenticate our peer (start server).
- */
- void
- ChapAuthPeer(unit)
- int unit;
- {
- chap_state *cstate = &chap[unit];
-
- cstate->flags &= ~CHAPF_APPENDING; /* Clear pending flag */
-
- /* Already authenticat{ed,ing}? */
- if (cstate->serverstate == CHAPSS_LISTEN ||
- cstate->serverstate == CHAPSS_OPEN)
- return;
-
- /* Lower layer up? */
- if (!(cstate->flags & CHAPF_LOWERUP)) {
- cstate->flags |= CHAPF_APPENDING; /* Wait for desired event */
- return;
- }
- cstate->serverstate = CHAPSS_LISTEN;
- }
-
-
- /*
- * ChapTimeout - Timeout expired.
- */
- static void
- ChapTimeout(arg)
- caddr_t arg;
- {
- chap_state *cstate = (chap_state *) arg;
-
- /* if we aren't sending challenges, don't worry. then again we */
- /* probably shouldn't be here either */
- if (cstate->clientstate != CHAPCS_CHALLENGE_SENT)
- return;
-
- ChapSendChallenge(cstate); /* Send challenge */
- TIMEOUT(ChapTimeout, (caddr_t) cstate, cstate->timeouttime);
- ++cstate->retransmits;
- }
-
-
- /*
- * ChapLowerUp - The lower layer is up.
- *
- * Start up if we have pending requests.
- */
- void
- ChapLowerUp(unit)
- int unit;
- {
- chap_state *cstate = &chap[unit];
-
- cstate->flags |= CHAPF_LOWERUP;
- if (cstate->flags & CHAPF_AWPPENDING) /* were we attempting authwithpeer? */
- ChapAuthWithPeer(unit); /* Try it now */
- if (cstate->flags & CHAPF_APPENDING) /* or authpeer? */
- ChapAuthPeer(unit);
- }
-
-
- /*
- * ChapLowerDown - The lower layer is down.
- *
- * Cancel all timeouts.
- */
- void
- ChapLowerDown(unit)
- int unit;
- {
- chap_state *cstate = &chap[unit];
-
- cstate->flags &= ~CHAPF_LOWERUP;
-
- if (cstate->clientstate == CHAPCS_CHALLENGE_SENT) /* Timeout pending? */
- UNTIMEOUT(ChapTimeout, (caddr_t) cstate); /* Cancel timeout */
-
- if (cstate->serverstate == CHAPSS_OPEN) /* have we successfully authed? */
- LOGOUT(unit);
- cstate->clientstate = CHAPCS_CLOSED;
- cstate->serverstate = CHAPSS_CLOSED;
- }
-
-
- /*
- * ChapProtocolReject - Peer doesn't grok CHAP.
- */
- void
- ChapProtocolReject(unit)
- int unit;
- {
- ChapLowerDown(unit); /* shutdown chap */
-
-
- /* Note: should we bail here if chap is required? */
- }
-
-
- /*
- * ChapInput - Input CHAP packet.
- */
- void
- ChapInput(unit, inpacket, packet_len)
- int unit;
- u_char *inpacket;
- int packet_len;
- {
- chap_state *cstate = &chap[unit];
- u_char *inp;
- u_char code, id;
- int len;
-
- /*
- * Parse header (code, id and length).
- * If packet too short, drop it.
- */
- inp = inpacket;
- if (packet_len < CHAP_HEADERLEN) {
- CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."))
- return;
- }
- GETCHAR(code, inp);
- GETCHAR(id, inp);
- GETSHORT(len, inp);
- if (len < CHAP_HEADERLEN) {
- CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."))
- return;
- }
- if (len > packet_len) {
- CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."))
- return;
- }
- len -= CHAP_HEADERLEN;
-
- /*
- * Action depends on code.
- */
- switch (code) {
- case CHAP_CHALLENGE:
- ChapReceiveChallenge(cstate, inp, id, len);
- break;
-
- case CHAP_RESPONSE:
- ChapReceiveResponse(cstate, inp, id, len);
- break;
-
- case CHAP_FAILURE:
- ChapReceiveFailure(cstate, inp, id, len);
- break;
-
- case CHAP_SUCCESS:
- ChapReceiveSuccess(cstate, inp, id, len);
- break;
-
- default: /* Need code reject? */
- syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
- break;
- }
- }
-
-
- /*
- * ChapReceiveChallenge - Receive Challenge.
- */
- static void
- ChapReceiveChallenge(cstate, inp, id, len)
- chap_state *cstate;
- u_char *inp;
- u_char id;
- int len;
- {
- u_char rchallenge_len;
- u_char *rchallenge;
- u_char secret[MAX_SECRET_LEN];
- int secret_len;
- u_char rhostname[256];
- u_char buf[256];
- MD5_CTX mdContext;
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id))
- if (cstate->serverstate != CHAPSS_LISTEN) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received challenge but not in listen state"))
- return;
- }
-
- if (len < 2) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."))
- return;
- }
- GETCHAR(rchallenge_len, inp);
- len -= sizeof (u_char) + rchallenge_len ;
- if (len < 0) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."))
- return;
- }
- rchallenge = inp;
- INCPTR(rchallenge_len, inp);
-
- BCOPY(inp, rhostname, len);
- rhostname[len] = '\000';
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s",
- rhostname))
- GETSECRET(rhostname, secret, &secret_len);/* get secret for specified host */
-
- BCOPY(rchallenge, buf, rchallenge_len); /* copy challenge into buffer */
- BCOPY(secret, buf + rchallenge_len, secret_len); /* append secret */
-
- /* generate MD based on negotiated type */
-
- switch (lcp_hisoptions[cstate->unit].chap_mdtype) {
-
- case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
- MD5Init(&mdContext);
- MD5Update(&mdContext, buf, rchallenge_len + secret_len);
- MD5Final(&mdContext);
- ChapSendResponse(cstate, id, &mdContext.digest[0], MD5_SIGNATURE_SIZE);
- break;
-
- default:
- CHAPDEBUG((LOG_INFO, "unknown digest type %d",
- lcp_hisoptions[cstate->unit].chap_mdtype))
- }
-
- }
-
-
- /*
- * ChapReceiveResponse - Receive and process response.
- */
- static void
- ChapReceiveResponse(cstate, inp, id, len)
- chap_state *cstate;
- u_char *inp;
- u_char id;
- int len;
- {
- u_char *remmd, remmd_len;
- u_char secret[MAX_SECRET_LEN];
- int secret_len;
- u_char chal_len = cstate->chal_len;
- u_char code;
- u_char rhostname[256];
- u_char buf[256];
- MD5_CTX mdContext;
- u_char msg[256], msglen;
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id))
-
- /* sanity check */
- if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received response but did not send a challenge"))
- return;
- }
-
- if (len < 2) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."))
- return;
- }
- GETCHAR(remmd_len, inp); /* get length of MD */
- len -= sizeof (u_char) + remmd_len ;
-
- if (len < 0) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."))
- return;
- }
-
- remmd = inp; /* get pointer to MD */
- INCPTR(remmd_len, inp);
-
- BCOPY(inp, rhostname, len);
- rhostname[len] = '\000';
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
- rhostname))
-
- GETSECRET(rhostname, secret, &secret_len);/* get secret for specified host */
-
- BCOPY(cstate->chal_str, buf, chal_len); /* copy challenge */
- /* into buffer */
- BCOPY(secret, buf + chal_len, secret_len); /* append secret */
-
- /* generate MD based on negotiated type */
-
- switch (lcp_gotoptions[cstate->unit].chap_mdtype) {
-
- case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
- MD5Init(&mdContext);
- MD5Update(&mdContext, buf, chal_len + secret_len);
- MD5Final(&mdContext);
-
- /* compare local and remote MDs and send the appropriate status */
-
- if (bcmp (&mdContext.digest[0], remmd, MD5_SIGNATURE_SIZE))
- code = CHAP_FAILURE; /* they ain't the same */
- else
- code = CHAP_SUCCESS; /* they are the same! */
- break;
-
- default:
- CHAPDEBUG((LOG_INFO, "unknown digest type %d",
- lcp_gotoptions[cstate->unit].chap_mdtype))
- }
- if (code == CHAP_SUCCESS)
- sprintf(msg, "Welcome to %s.", hostname);
- else
- sprintf(msg, "I don't like you. Go 'way.");
- msglen = strlen(msg);
- ChapSendStatus(cstate, code, id, msg, msglen);
-
- /* only crank up IPCP when either we aren't doing PAP, or if we are, */
- /* that it is in open state */
-
- if (code == CHAP_SUCCESS) {
- cstate->serverstate = CHAPSS_OPEN;
- if (!lcp_hisoptions[cstate->unit].neg_upap ||
- (lcp_hisoptions[cstate->unit].neg_upap &&
- upap[cstate->unit].us_serverstate == UPAPSS_OPEN ))
- ipcp_activeopen(cstate->unit); /* Start IPCP */
- }
- }
- /*
- * ChapReceiveSuccess - Receive Success
- */
- /* ARGSUSED */
- static void
- ChapReceiveSuccess(cstate, inp, id, len)
- chap_state *cstate;
- u_char *inp;
- u_char id;
- int len;
- {
- u_char msglen;
- u_char *msg;
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id))
-
- if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: received success, but did not send a challenge."))
- return;
- }
-
- /*
- * Parse message.
- */
- if (len < sizeof (u_char)) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: rcvd short packet."))
- return;
- }
- GETCHAR(msglen, inp);
- len -= sizeof (u_char);
- if (len < msglen) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: rcvd short packet."))
- return;
- }
- msg = inp;
- PRINTMSG(msg, msglen);
-
- cstate->clientstate = CHAPCS_OPEN;
-
- /* only crank up IPCP when either we aren't doing PAP, or if we are, */
- /* that it is in open state */
-
- if (!lcp_gotoptions[cstate->unit].neg_chap ||
- (lcp_gotoptions[cstate->unit].neg_chap &&
- upap[cstate->unit].us_serverstate == UPAPCS_OPEN ))
- ipcp_activeopen(cstate->unit); /* Start IPCP */
- }
-
-
- /*
- * ChapReceiveFailure - Receive failure.
- */
- /* ARGSUSED */
- static void
- ChapReceiveFailure(cstate, inp, id, len)
- chap_state *cstate;
- u_char *inp;
- u_char id;
- int len;
- {
- u_char msglen;
- u_char *msg;
-
- CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id))
- if (cstate->clientstate != CHAPCS_CHALLENGE_SENT) /* XXX */
- return;
-
- /*
- * Parse message.
- */
- if (len < sizeof (u_char)) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: rcvd short packet."))
- return;
- }
- GETCHAR(msglen, inp);
- len -= sizeof (u_char);
- if (len < msglen) {
- CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: rcvd short packet."))
- return;
- }
- msg = inp;
- PRINTMSG(msg, msglen);
-
- cstate->flags &= ~CHAPF_UPVALID; /* Clear valid flag */
- cstate->clientstate = CHAPCS_CLOSED; /* Pretend for a moment */
- ChapAuthWithPeer(cstate->unit); /* Restart */
- }
-
-
- /*
- * ChapSendChallenge - Send an Authenticate challenge.
- */
- static void
- ChapSendChallenge(cstate)
- chap_state *cstate;
- {
- u_char *outp;
- u_char chal_len;
- int outlen;
-
- /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
- MAX_CHALLENGE_LENGTH */
- cstate->chal_len = (unsigned) ((drand48() *
- (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
- MIN_CHALLENGE_LENGTH);
- chal_len = cstate->chal_len;
-
- outlen = CHAP_HEADERLEN + 2 * sizeof (u_char) + chal_len + hostname_len;
- outp = outpacket_buf;
-
- MAKEHEADER(outp, CHAP); /* paste in a CHAP header */
-
- PUTCHAR(CHAP_CHALLENGE, outp);
- PUTCHAR(++cstate->id, outp);
- PUTSHORT(outlen, outp);
-
- PUTCHAR(chal_len, outp); /* put length of challenge */
-
- ChapGenChallenge(chal_len, cstate->chal_str); /* generate a challenge string */
-
- BCOPY(cstate->chal_str, outp, chal_len); /* copy it the the output buffer */
- INCPTR(chal_len, outp);
-
- BCOPY(hostname, outp, hostname_len); /* append hostname */
- INCPTR(hostname_len, outp);
-
- output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
-
- CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->id))
- cstate->clientstate |= CHAPCS_CHALLENGE_SENT;
- }
-
-
- /*
- * ChapSendStatus - Send a status response (ack or nak).
- */
- static void
- ChapSendStatus(cstate, code, id, msg, msglen)
- chap_state *cstate;
- u_char code, id;
- u_char *msg;
- int msglen;
- {
- u_char *outp;
- int outlen;
-
- outlen = CHAP_HEADERLEN + msglen;
- outp = outpacket_buf;
-
- MAKEHEADER(outp, CHAP); /* paste in a header */
-
- PUTCHAR(code, outp);
- PUTCHAR(id, outp);
- PUTSHORT(outlen, outp);
- BCOPY(msg, outp, msglen);
- output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
-
- CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, id))
- }
-
- /*
- * ChapGenChallenge is used to generate a pseudo-random challenge string of
- * a pseudo-random length between min_len and max_len and return the
- * challenge string, and the message digest of the secret appended to
- * the challenge string. the message digest type is specified by mdtype.
- *
- * It returns with the string in the caller-supplied buffer str (which
- * should be instantiated with a length of max_len + 1), and the
- * length of the generated string into chal_len.
- *
- */
-
- static void
- ChapGenChallenge(chal_len, str)
- u_char chal_len;
- u_char * str;
- {
- u_char * ptr = str;
- unsigned int i;
-
- /* generate a random string */
-
- for (i = 0; i < chal_len; i++ )
- *ptr++ = (char) (drand48() * 0xff);
-
- *ptr = 0; /* null terminate it so we can printf it */
- }
- /*
- * ChapSendResponse - send a response packet with the message
- * digest specified by md and md_len
- */
- /* ARGSUSED */
- static void
- ChapSendResponse(cstate, id, md, md_len)
- chap_state *cstate;
- u_char id;
- u_char *md;
- int md_len;
- {
- u_char *outp;
- int outlen;
-
- outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + hostname_len;
- outp = outpacket_buf;
- MAKEHEADER(outp, CHAP);
-
- PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
- PUTCHAR(id, outp); /* copy id from challenge packet */
- PUTSHORT(outlen, outp); /* packet length */
-
- PUTCHAR(md_len, outp); /* length of MD */
-
- BCOPY(md, outp, md_len); /* copy MD to buffer */
- INCPTR(md_len, outp);
-
- BCOPY(hostname, outp, hostname_len); /* append hostname */
- INCPTR(hostname_len, outp);
-
- output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN); /* bomb's away! */
- }
-