]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/chap.c
Large patch from Frank Cusack <fcusack@fcusack.com> to add proper
[ppp.git] / pppd / chap.c
index 4fd17628b1b09316240e52f00bc6921f6a892e9c..25427e2d15b133c96ccefd147b4270fe284ea0b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * chap.c - Challenge Handshake Authentication Protocol.
+ * chap_ms.c - Challenge Handshake Authentication Protocol.
  *
  * Copyright (c) 1993 The Australian National University.
  * All rights reserved.
@@ -33,9 +33,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#ifndef lint
-static char rcsid[] = "$Id: chap.c,v 1.21 1999/03/19 01:20:19 paulus Exp $";
-#endif
+#define RCSID  "$Id: chap.c,v 1.27 2002/03/01 14:39:18 dfs Exp $"
 
 /*
  * TODO:
@@ -53,16 +51,30 @@ static char rcsid[] = "$Id: chap.c,v 1.21 1999/03/19 01:20:19 paulus Exp $";
 #include "chap_ms.h"
 #endif
 
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to validate CHAP challenge */
+int (*chap_auth_hook) __P((char *user,
+                          u_char *remmd,
+                          int remmd_len,
+                          chap_state *cstate)) = NULL;
+
+static const char rcsid[] = RCSID;
+
 /*
  * Command-line options.
  */
 static option_t chap_option_list[] = {
     { "chap-restart", o_int, &chap[0].timeouttime,
-      "Set timeout for CHAP" },
+      "Set timeout for CHAP", OPT_PRIO },
     { "chap-max-challenge", o_int, &chap[0].max_transmits,
-      "Set max #xmits for challenge" },
+      "Set max #xmits for challenge", OPT_PRIO },
     { "chap-interval", o_int, &chap[0].chal_interval,
-      "Set interval for rechallenge" },
+      "Set interval for rechallenge", OPT_PRIO },
 #ifdef MSLANMAN
     { "ms-lanman", o_bool, &ms_lanman,
       "Use LanMan passwd when using MS-CHAP", 1 },
@@ -161,7 +173,7 @@ ChapAuthWithPeer(unit, our_name, digest)
 
     /*
      * We get here as a result of LCP coming up.
-     * So even if CHAP was open before, we will 
+     * So even if CHAP was open before, we will
      * have to re-authenticate ourselves.
      */
     cstate->clientstate = CHAPCS_LISTEN;
@@ -178,7 +190,7 @@ ChapAuthPeer(unit, our_name, digest)
     int digest;
 {
     chap_state *cstate = &chap[unit];
-  
+
     cstate->chal_name = our_name;
     cstate->chal_type = digest;
 
@@ -203,7 +215,7 @@ ChapChallengeTimeout(arg)
     void *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->serverstate != CHAPSS_INITIAL_CHAL &&
@@ -268,7 +280,7 @@ ChapLowerUp(unit)
     int unit;
 {
     chap_state *cstate = &chap[unit];
-  
+
     if (cstate->clientstate == CHAPCS_INITIAL)
        cstate->clientstate = CHAPCS_CLOSED;
     else if (cstate->clientstate == CHAPCS_PENDING)
@@ -294,7 +306,7 @@ ChapLowerDown(unit)
     int unit;
 {
     chap_state *cstate = &chap[unit];
-  
+
     /* Timeout(s) pending?  Cancel if so. */
     if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
        cstate->serverstate == CHAPSS_RECHALLENGE)
@@ -342,7 +354,7 @@ ChapInput(unit, inpacket, packet_len)
     u_char *inp;
     u_char code, id;
     int len;
-  
+
     /*
      * Parse header (code, id and length).
      * If packet too short, drop it.
@@ -364,7 +376,7 @@ ChapInput(unit, inpacket, packet_len)
        return;
     }
     len -= CHAP_HEADERLEN;
-  
+
     /*
      * Action depends on code (as in fact it usually does :-).
      */
@@ -372,11 +384,11 @@ ChapInput(unit, inpacket, packet_len)
     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;
@@ -409,7 +421,7 @@ ChapReceiveChallenge(cstate, inp, id, len)
     char rhostname[256];
     MD5_CTX mdContext;
     u_char hash[MD5_SIGNATURE_SIZE];
+
     if (cstate->clientstate == CHAPCS_CLOSED ||
        cstate->clientstate == CHAPCS_PENDING) {
        CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
@@ -457,7 +469,7 @@ ChapReceiveChallenge(cstate, inp, id, len)
     cstate->resp_transmits = 0;
 
     /*  generate MD based on negotiated type */
-    switch (cstate->resp_type) { 
+    switch (cstate->resp_type) {
 
     case CHAP_DIGEST_MD5:
        MD5Init(&mdContext);
@@ -552,34 +564,70 @@ ChapReceiveResponse(cstate, inp, id, len)
      * do the hash ourselves, and compare the result.
      */
     code = CHAP_FAILURE;
-    if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
-                   cstate->chal_name, secret, &secret_len, 1)) {
-       warn("No CHAP secret found for authenticating %q", rhostname);
-    } else {
 
-       /*  generate MD based on negotiated type */
-       switch (cstate->chal_type) { 
-
-       case CHAP_DIGEST_MD5:           /* only MD5 is defined for now */
-           if (remmd_len != MD5_SIGNATURE_SIZE)
-               break;                  /* it's not even the right length */
-           MD5Init(&mdContext);
-           MD5Update(&mdContext, &cstate->chal_id, 1);
-           MD5Update(&mdContext, secret, secret_len);
-           MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
-           MD5Final(hash, &mdContext); 
-
-           /* compare local and remote MDs and send the appropriate status */
-           if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
-               code = CHAP_SUCCESS;    /* they are the same! */
-           break;
+    /* If a plugin will verify the response, let the plugin do it. */
+    if (chap_auth_hook) {
+       code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
+                                  remmd, (int) remmd_len,
+                                  cstate );
+    } else {
+       if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
+                       cstate->chal_name, secret, &secret_len, 1)) {
+           warn("No CHAP secret found for authenticating %q", rhostname);
+       } else {
+
+           /*  generate MD based on negotiated type */
+           switch (cstate->chal_type) {
+
+           case CHAP_DIGEST_MD5:
+               if (remmd_len != MD5_SIGNATURE_SIZE)
+                   break;                      /* not even the right length */
+               MD5Init(&mdContext);
+               MD5Update(&mdContext, &cstate->chal_id, 1);
+               MD5Update(&mdContext, secret, secret_len);
+               MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+               MD5Final(hash, &mdContext);
+
+               /* compare MDs and send the appropriate status */
+               if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+                   code = CHAP_SUCCESS;        /* they are the same! */
+               break;
 
-       default:
-           CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
+#ifdef CHAPMS
+           case CHAP_MICROSOFT:
+           {
+               int response_offset, response_size;
+
+               if (remmd_len != MS_CHAP_RESPONSE_LEN)
+                   break;                      /* not even the right length */
+               ChapMS(cstate, cstate->challenge, cstate->chal_len,
+                      secret, secret_len);
+
+               /* Determine which part of response to verify against */
+               if ((u_char *) (remmd + offsetof(MS_ChapResponse, UseNT))) {
+                   response_offset = offsetof(MS_ChapResponse, NTResp);
+                   response_size = sizeof(((MS_ChapResponse *) remmd)->NTResp);
+               } else {
+                   response_offset = offsetof(MS_ChapResponse, LANManResp);
+                   response_size =
+                       sizeof(((MS_ChapResponse *) remmd)->LANManResp);
+               }
+
+               /* compare MDs and send the appropriate status */
+               if (memcmp(cstate->response + response_offset,
+                   remmd + response_offset, response_size) == 0)
+                   code = CHAP_SUCCESS;        /* they are the same! */
+               break;
+           }
+#endif /* CHAPMS */
+
+           default:
+               CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
+           }
        }
-    }
 
-    BZERO(secret, sizeof(secret));
+       BZERO(secret, sizeof(secret));
+    }
     ChapSendStatus(cstate, code);
 
     if (code == CHAP_SUCCESS) {
@@ -692,7 +740,7 @@ ChapSendChallenge(cstate)
     BCOPY(cstate->chal_name, outp, name_len);  /* append hostname */
 
     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
-  
+
     TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
     ++cstate->chal_transmits;
 }
@@ -720,7 +768,7 @@ ChapSendStatus(cstate, code)
     outp = outpacket_buf;
 
     MAKEHEADER(outp, PPP_CHAP);        /* paste in a header */
-  
+
     PUTCHAR(code, outp);
     PUTCHAR(cstate->chal_id, outp);
     PUTSHORT(outlen, outp);
@@ -739,21 +787,39 @@ static void
 ChapGenChallenge(cstate)
     chap_state *cstate;
 {
-    int chal_len;
+    int chal_len = 0; /* Avoid compiler warning */
     u_char *ptr = cstate->challenge;
-    unsigned int i;
+    int i;
+
+    switch (cstate->chal_type) {
+    case CHAP_DIGEST_MD5:
+       /*
+        * pick a random challenge length between MIN_CHALLENGE_LENGTH and
+        * MAX_CHALLENGE_LENGTH
+        */
+       chal_len = (unsigned) ((drand48() *
+                               (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+                               MIN_CHALLENGE_LENGTH);
+       break;
+
+#ifdef CHAPMS
+    case CHAP_MICROSOFT:
+       /* MS-CHAP is fixed to an 8 octet challenge. */
+       chal_len = 8;
+       break;
+#endif
+    default:
+       fatal("ChapGenChallenge: Unsupported challenge type %d",
+             (int) cstate->chal_type);
+       break;
+    }
 
-    /* pick a random challenge length between MIN_CHALLENGE_LENGTH and 
-       MAX_CHALLENGE_LENGTH */  
-    chal_len =  (unsigned) ((drand48() *
-                            (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
-                           MIN_CHALLENGE_LENGTH);
     cstate->chal_len = chal_len;
     cstate->chal_id = ++cstate->id;
     cstate->chal_transmits = 0;
 
     /* generate a random string */
-    for (i = 0; i < chal_len; i++ )
+    for (i = 0; i < chal_len; i++)
        *ptr++ = (char) (drand48() * 0xff);
 }