Patch from Frank Cusack to add support for MSCHAPv2.
authorDavid F. Skoll <dfs@roaringpenguin.com>
Tue, 5 Mar 2002 15:14:06 +0000 (15:14 +0000)
committerDavid F. Skoll <dfs@roaringpenguin.com>
Tue, 5 Mar 2002 15:14:06 +0000 (15:14 +0000)
Enhanced radiusclient to support INCLUDE lines in dictionary.

19 files changed:
README.MSCHAP80
README.MSCHAP81 [new file with mode: 0644]
pppd/Makefile.linux
pppd/Makefile.osf
pppd/auth.c
pppd/chap.c
pppd/chap.h
pppd/chap_ms.c
pppd/chap_ms.h
pppd/lcp.c
pppd/plugins/radius/pppd-radius.8
pppd/plugins/radius/radius.c
pppd/plugins/radius/radiusclient/etc/dictionary
pppd/plugins/radius/radiusclient/include/radiusclient.h
pppd/plugins/radius/radiusclient/lib/dict.c
pppd/pppd.8
pppd/sha1.c [new file with mode: 0644]
pppd/sha1.h [new file with mode: 0644]
pppd/utils.c

index fe7a019416dd70032500244bdf302d75fff565e5..c77d7696d1d468fa746e33f66c1be4f70070116e 100644 (file)
@@ -162,7 +162,7 @@ Assuming that everything else has been configured correctly for PPP and
 CHAP, the MS-CHAP-specific problems you're likely to encounter are mostly
 related to your Windows NT account and its settings.  A Microsoft server
 returns error codes in its CHAP response.  The following are extracted from
-Microsoft's "chapexts.txt" file referenced above:
+RFC 2433:
 
  646 ERROR_RESTRICTED_LOGON_HOURS
  647 ERROR_ACCT_DISABLED
diff --git a/README.MSCHAP81 b/README.MSCHAP81
new file mode 100644 (file)
index 0000000..1d415a9
--- /dev/null
@@ -0,0 +1,65 @@
+PPP Support for Microsoft's CHAP-81
+===================================
+
+Frank Cusack           frank@google.com
+
+Some text verbatim from README.MSCHAP80,
+by Eric Rosenquist, rosenqui@strataware.com
+
+INTRODUCTION
+
+First, please read README.MSCHAP80; almost everything there applies here.
+MS-CHAP was basically devised by Microsoft because rather than store
+plaintext passwords, they (Microsoft) store the md4 hash of passwords.
+It provides no advantage over standard CHAP, since the hash is used
+as plaintext-equivalent.  (Well, the Change-Password packet is arguably
+an advantage.)  It does introduce a significant weakness if the LM hash
+is used.  Additionally, the format of the failure packet potentially
+gives information to an attacker.  The weakness of the LM hash is partly
+address in RFC 2433, which deprecates its use.
+
+MS-CHAPv2 adds 2 benefits to MS-CHAP.  (1) The LM hash is no longer
+used.  (2) Mutual authentication is required.  Note that the mutual
+authentication in MS-CHAPv2 is different than the case where both PPP
+peers require authentication from the other; the former proves that
+the server has access to the client's password, the latter proves that
+the server has access to a secret which the client also has -- which
+may or may not be the same as the client's password (but should not be
+the same, per RFC 1994).  Whether this provides any actual benefit is
+outside the scope of this document.  The details of MS-CHAPv2 can be
+found in the document:
+
+    <http://www.ietf.org/rfc/rfc2759.txt>
+
+
+BUILDING THE PPPD
+
+In addition to the requirements for MS-CHAP, MS-CHAPv2 uses the SHA-1
+hash algorithm.  A public domain implementation is provided with pppd.
+
+
+TROUBLESHOOTING
+
+Assuming that everything else has been configured correctly for PPP and
+CHAP, the MS-CHAPv2-specific problems you're likely to encounter are mostly
+related to your Windows NT account and its settings.  A Microsoft server
+returns error codes in its CHAP response.  The following are extracted from
+RFC 2759:
+
+ 646 ERROR_RESTRICTED_LOGON_HOURS
+ 647 ERROR_ACCT_DISABLED
+ 648 ERROR_PASSWD_EXPIRED
+ 649 ERROR_NO_DIALIN_PERMISSION
+ 691 ERROR_AUTHENTICATION_FAILURE
+ 709 ERROR_CHANGING_PASSWORD
+
+You'll see these in your pppd log as a line similar to:
+
+   Remote message: No dialin permission
+
+Previously, pppd would log this as:
+
+   Remote message: E=649 R=0
+
+Now, the text message is logged (both for MS-CHAP and MS-CHAPv2).
+
index d16a71ef747a6a5982570c5f722d8dd28c329370..aea5fb9e9590f6c52344ed4bfc8b64efabb9b9cc 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.45 2002/03/04 14:59:51 dfs Exp $
+# $Id: Makefile.linux,v 1.46 2002/03/05 15:14:04 dfs Exp $
 #
 
 # Default installation locations
@@ -9,12 +9,12 @@ MANDIR = /usr/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
           ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-          demand.c utils.c tty.c
+          demand.c utils.c tty.c sha1.c
 HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
-          ipxcp.h cbcp.h tdb.h
+          ipxcp.h cbcp.h tdb.h sha1.h
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o
+          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o sha1.o
 
 all: pppd
 
index 825e73abc19df44aa81edb95f4bff0e1301c3a2c..63f4eb9697077612fd920e3a8bbcbe9fc3b8ffe1 100644 (file)
@@ -1,16 +1,16 @@
 #
 # pppd makefile for OSF/1 on DEC Alpha
-# $Id: Makefile.osf,v 1.10 1999/04/27 22:31:18 varadhan Exp $
+# $Id: Makefile.osf,v 1.11 2002/03/05 15:14:04 dfs Exp $
 #
 
 BINDIR = /usr/local/etc
 MANDIR = /usr/local/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-       auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c
+       auth.c options.c demand.c utils.c sys-osf.c md4.c chap_ms.c sha1.c
 
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-       auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o
+       auth.o options.o demand.o utils.o sys-osf.o md4.o chap_ms.o sha1.o
 
 CC = cc
 #DEBUG_FLAGS = -DDEBUGALL
index 4f9c29788940905657e6eaf50dc3d6ffbdf6cf5e..61b50e5b95d68e9bf249f683ab2b9c6faf504e0a 100644 (file)
@@ -32,7 +32,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.74 2002/03/01 14:39:18 dfs Exp $"
+#define RCSID  "$Id: auth.c,v 1.75 2002/03/05 15:14:04 dfs Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -171,8 +171,10 @@ bool refuse_pap = 0;               /* Don't wanna auth. ourselves with PAP */
 bool refuse_chap = 0;          /* Don't wanna auth. ourselves with CHAP */
 #ifdef CHAPMS
 bool refuse_mschap = 0;                /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0;     /* Don't wanna auth. ourselves with MS-CHAPv2 */
 #else
 bool refuse_mschap = 1;                /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1;     /* Don't wanna auth. ourselves with MS-CHAPv2 */
 #endif
 bool usehostname = 0;          /* Use hostname for our_name */
 bool auth_required = 0;                /* Always require authentication from peer */
@@ -248,6 +250,14 @@ option_t auth_options[] = {
       "Require MS-CHAP authentication from peer",
       OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT,
       &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
+    { "require-mschap-v2", o_bool, &lcp_wantoptions[0].neg_chap,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT_V2,
+      &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap-v2", o_bool, &lcp_wantoptions[0].neg_chap,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT_V2,
+      &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype },
 #endif
 
     { "refuse-pap", o_bool, &refuse_pap,
@@ -263,12 +273,19 @@ option_t auth_options[] = {
       &lcp_allowoptions[0].chap_mdtype },
 #ifdef CHAPMS
     { "refuse-mschap", o_bool, &refuse_mschap,
-      "Don't agree to auth to peer with MS-CHAP", OPT_A2CLRB | MDTYPE_MICROSOFT,
-      &lcp_allowoptions[0].chap_mdtype },
+      "Don't agree to auth to peer with MS-CHAP",
+      OPT_A2CLRB | MDTYPE_MICROSOFT, &lcp_allowoptions[0].chap_mdtype },
     { "-mschap", o_bool, &refuse_mschap,
       "Don't allow MS-CHAP authentication with peer",
       OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
       &lcp_allowoptions[0].chap_mdtype },
+    { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't agree to auth to peer with MS-CHAPv2",
+      OPT_A2CLRB | MDTYPE_MICROSOFT_V2, &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't allow MS-CHAPv2 authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
 #endif
 
     { "name", o_string, our_name,
@@ -917,7 +934,7 @@ auth_reset(unit)
     lcp_options *ao = &lcp_allowoptions[0];
 
     ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
-    ao->neg_chap = (!refuse_chap || !refuse_mschap)
+    ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
        && (passwd[0] != 0
            || have_chap_secret(user, (explicit_remote? remote_name: NULL),
                                0, NULL));
index 208a144fc8569051fbf18f2925c8aa93daf0449b..21b0280323577b5bc10896237a5bb267248a7729 100644 (file)
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: chap.c,v 1.28 2002/03/04 14:59:51 dfs Exp $"
+#define RCSID  "$Id: chap.c,v 1.29 2002/03/05 15:14:04 dfs Exp $"
 
 /*
  * TODO:
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/time.h>
@@ -483,9 +484,17 @@ ChapReceiveChallenge(cstate, inp, id, len)
 
 #ifdef CHAPMS
     case CHAP_MICROSOFT:
-       ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+       ChapMS(cstate, rchallenge, secret, secret_len,
+              (MS_ChapResponse *) cstate->response);
+       cstate->resp_length = MS_CHAP_RESPONSE_LEN;
        break;
-#endif
+
+    case CHAP_MICROSOFT_V2:
+       ChapMS2(cstate, rchallenge, NULL, cstate->resp_name, secret, secret_len,
+               (MS_Chap2Response *) cstate->response, cstate->earesponse);
+       cstate->resp_length = MS_CHAP2_RESPONSE_LEN;
+       break;
+#endif /* CHAPMS */
 
     default:
        CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
@@ -589,7 +598,7 @@ ChapReceiveResponse(cstate, inp, id, len)
                MD5Final(hash, &mdContext);
 
                /* compare MDs and send the appropriate status */
-               if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+               if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0)
                    code = CHAP_SUCCESS;        /* they are the same! */
                break;
 
@@ -598,27 +607,55 @@ ChapReceiveResponse(cstate, inp, id, len)
            {
                int response_offset, response_size;
                MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
+               MS_ChapResponse md;
 
                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 (rmd->UseNT[0]) {
                    response_offset = offsetof(MS_ChapResponse, NTResp);
                    response_size = sizeof(rmd->NTResp);
                } else {
+#ifdef MSLANMAN
                    response_offset = offsetof(MS_ChapResponse, LANManResp);
                    response_size = sizeof(rmd->LANManResp);
+#else
+                   /* Should really propagate this into the error packet. */
+                   notice("Peer request for LANMAN auth not supported");
+                   break;
+#endif /* MSLANMAN */
                }
 
+               /* Generate the expected response. */
+               ChapMS(cstate, cstate->challenge, secret, secret_len, &md);
+
                /* compare MDs and send the appropriate status */
-               if (memcmp(cstate->response + response_offset,
-                   remmd + response_offset, response_size) == 0)
+               if (memcmp(&md + response_offset,
+                          remmd + response_offset, response_size) == 0)
                    code = CHAP_SUCCESS;        /* they are the same! */
                break;
            }
+
+           case CHAP_MICROSOFT_V2:
+           {
+               MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
+               MS_Chap2Response md;
+
+               if (remmd_len != MS_CHAP2_RESPONSE_LEN)
+                   break;                      /* not even the right length */
+
+               /* Generate the expected response and our mutual auth. */
+               ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
+                       (explicit_remote? remote_name: rhostname),
+                       secret, secret_len, &md,
+                       cstate->saresponse);
+
+               /* compare MDs and send the appropriate status */
+               if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
+                   code = CHAP_SUCCESS;        /* yay! */
+               break;
+           }
 #endif /* CHAPMS */
 
            default:
@@ -670,6 +707,37 @@ ChapReceiveSuccess(cstate, inp, id, len)
 
     UNTIMEOUT(ChapResponseTimeout, cstate);
 
+#ifdef CHAPMS
+    /*
+     * For MS-CHAPv2, we must verify that the peer knows our secret.
+     */
+    if (cstate->resp_type == CHAP_MICROSOFT_V2) {
+       if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) {
+           inp += 2; len -= 2;
+           if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) {
+               /* Authenticator Response matches. */
+               inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+               len -= MS_AUTH_RESPONSE_LENGTH;
+               if ((len >= 3) && !strncmp(inp, " M=", 3)) {
+                   inp += 3; len -= 3; /* Eat the delimiter */
+               } else if (len) {
+                   /* Packet has extra text which does not begin " M=" */
+                   error("MS-CHAPv2 Success packet is badly formed.");
+                   auth_withpeer_fail(cstate->unit, PPP_CHAP);
+               }
+           } else {
+               /* Authenticator Response did not match expected. */
+               error("MS-CHAPv2 mutual authentication failed.");
+               auth_withpeer_fail(cstate->unit, PPP_CHAP);
+           }
+       } else {
+           /* Packet does not start with "S=" */
+           error("MS-CHAPv2 Success packet is badly formed.");
+           auth_withpeer_fail(cstate->unit, PPP_CHAP);
+       }
+    }
+#endif
+
     /*
      * Print message.
      */
@@ -692,22 +760,103 @@ ChapReceiveFailure(cstate, inp, id, len)
     u_char id;
     int len;
 {
+    u_char *msg;
+    u_char *p = inp;
+
     if (cstate->clientstate != CHAPCS_RESPONSE) {
        /* don't know what this is */
        CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
        return;
     }
 
+#ifdef CHAPMS
+    /* We want a null-terminated string for strxxx(). */
+    msg = malloc(len + 1);
+    if (!msg) {
+       p = NULL;
+       notice("Out of memory in ChapReceiveFailure");
+       goto print_msg;
+    }
+    BCOPY(inp, msg, len);
+    p = msg + len; *p = '\0'; p = msg;
+#endif
+
     UNTIMEOUT(ChapResponseTimeout, cstate);
 
+#ifdef CHAPMS
+    if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
+       (cstate->resp_type == CHAP_MICROSOFT)) {
+       long error;
+
+       /*
+        * Deal with MS-CHAP formatted failure messages; just print the
+        * M=<message> part (if any).  For MS-CHAP we're not really supposed
+        * to use M=<message>, but it shouldn't hurt.  See ChapSendStatus().
+        */
+       if (!strncmp(p, "E=", 2))
+           error = strtol(p, NULL, 10); /* Remember the error code. */
+       else
+           goto print_msg; /* Message is badly formatted. */
+
+       if (len && ((p = strstr(p, " M=")) != NULL)) {
+           /* M=<message> field found. */
+           p += 3;
+       } else {
+           /* No M=<message>; use the error code. */
+           switch(error - MS_CHAP_ERROR_BASE) {
+           case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+               p = "Restricted logon hours";
+               break;
+
+           case MS_CHAP_ERROR_ACCT_DISABLED:
+               p = "Account disabled";
+               break;
+
+           case MS_CHAP_ERROR_PASSWD_EXPIRED:
+               p = "Password expired";
+               break;
+
+           case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+               p = "No dialin permission";
+               break;
+
+           case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+               p = "Authentication failure";
+               break;
+
+           case MS_CHAP_ERROR_CHANGING_PASSWORD:
+               /* Should never see this, we don't support Change Password. */
+               p = "Error changing password";
+               break;
+
+           default:
+               free(msg);
+               p = msg = malloc(len + 33);
+               if (!msg) {
+                   notice("Out of memory in ChapReceiveFailure");
+                   goto print_msg;
+               }
+               slprintf(p, len + 33, "Unknown authentication failure: %.*s",
+                        len, inp);
+               break;
+           }
+       }
+       len = strlen(p);
+    }
+#endif
+
     /*
      * Print message.
      */
-    if (len > 0)
-       PRINTMSG(inp, len);
+print_msg:
+    if (len > 0 && p != NULL)
+       PRINTMSG(p, len);
 
     error("CHAP authentication failed");
     auth_withpeer_fail(cstate->unit, PPP_CHAP);
+#ifdef CHAPMS
+    if (msg) free(msg);
+#endif
 }
 
 
@@ -748,6 +897,7 @@ ChapSendChallenge(cstate)
 
 /*
  * ChapSendStatus - Send a status response (ack or nak).
+ * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats.
  */
 static void
 ChapSendStatus(cstate, code)
@@ -755,13 +905,63 @@ ChapSendStatus(cstate, code)
     int code;
 {
     u_char *outp;
-    int outlen, msglen;
+    int i, outlen, msglen;
     char msg[256];
+    char *p, *q;
 
-    if (code == CHAP_SUCCESS)
-       slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
-    else
-       slprintf(msg, sizeof(msg), "I don't like you.  Go 'way.");
+    p = msg;
+    q = p + sizeof(msg); /* points 1 byte past msg */
+
+    if (code == CHAP_SUCCESS) {
+#ifdef CHAPMS
+       if (cstate->chal_type == CHAP_MICROSOFT_V2) {
+           /*
+            * Success message must be formatted as
+            *     "S=<auth_string> M=<message>"
+            * where
+            *     <auth_string> is the Authenticator Response (mutual auth)
+            *     <message> is a text message
+            */
+           slprintf(p, q - p, "S=");
+           p += 2;
+           slprintf(p, q - p, "%s", cstate->saresponse);
+           p += strlen(cstate->saresponse);
+           slprintf(p, q - p, " M=");
+           p += 3;
+       }
+#endif /* CHAPMS */
+
+       slprintf(p, q - p, "Welcome to %s.", hostname);
+    } else {
+#ifdef CHAPMS
+       if ((cstate->chal_type == CHAP_MICROSOFT_V2) ||
+           (cstate->chal_type == CHAP_MICROSOFT)) {
+           /*
+            * Failure message must be formatted as
+            *     "E=e R=r C=c V=v M=m"
+            * where
+            *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+            *     r = retry (we use 1, ok to retry)
+            *     c = challenge to use for next response, we reuse previous
+            *     v = Change Password version supported, we use 0
+            *     m = text message
+            *
+            * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore
+            * any extra text according to RFC 2433.  So we'll go the easy
+            * (read: lazy) route and include it always.
+            */
+           slprintf(p, q - p, "E=691 R=1 C=");
+           p += 12;
+           for (i = 0; i < cstate->chal_len; i++)
+               sprintf(p + i * 2, "%02X", cstate->challenge[i]);
+           p += cstate->chal_len * 2;
+           slprintf(p, q - p, " V=0 M=");
+           p += 7;
+       }
+#endif /* CHAPMS */
+
+       slprintf(p, q - p, "I don't like you.  Go 'way.");
+    }
     msglen = strlen(msg);
 
     outlen = CHAP_HEADERLEN + msglen;
@@ -807,6 +1007,11 @@ ChapGenChallenge(cstate)
        /* MS-CHAP is fixed to an 8 octet challenge. */
        chal_len = 8;
        break;
+
+    case CHAP_MICROSOFT_V2:
+       /* MS-CHAPv2 is fixed to a 16 octet challenge. */
+       chal_len = 16;
+       break;
 #endif
     default:
        fatal("ChapGenChallenge: Unsupported challenge type %d",
index d15b948c22780d1c6ff61cea8283a63621b726d2..13f05113bedbab2c5b06e3bfac715b643f6e65b4 100644 (file)
@@ -30,7 +30,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: chap.h,v 1.10 2002/03/01 14:39:18 dfs Exp $
+ * $Id: chap.h,v 1.12 2002/04/02 13:54:59 dfs Exp $
  */
 
 #ifndef __CHAP_INCLUDE__
@@ -45,6 +45,7 @@
 #define CHAP_DIGEST_MD5                5       /* use MD5 algorithm */
 #define MD5_SIGNATURE_SIZE     16      /* 16 bytes in a MD5 message digest */
 #define CHAP_MICROSOFT         0x80    /* use Microsoft-compatible alg. */
+#define CHAP_MICROSOFT_V2      0x81    /* use Microsoft-compatible alg. */
 
 /*
  * Digest type and selection.
 
 /* bitmask of supported algorithms */
 #define MDTYPE_MD5             0x1
-#define MDTYPE_MICROSOFT       0x2
+#define MDTYPE_MICROSOFT_V2    0x2
+#define MDTYPE_MICROSOFT       0x4
 
 #ifdef CHAPMS
-#define MDTYPE_ALL (MDTYPE_MD5 | MDTYPE_MICROSOFT)
+#define MDTYPE_ALL (MDTYPE_MD5 | MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT)
 #else
 #define MDTYPE_ALL (MDTYPE_MD5)
 #endif
@@ -64,6 +66,7 @@
 /* Return the digest alg. ID for the most preferred digest type. */
 #define CHAP_DIGEST(mdtype) \
     ((mdtype) & MDTYPE_MD5)? CHAP_DIGEST_MD5: \
+    ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \
     ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \
     0
 
 /* Return the bit flag for a given digest algorithm ID. */
 #define CHAP_MDTYPE_D(digest) \
     ((digest) == CHAP_DIGEST_MD5)? MDTYPE_MD5: \
+    ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \
     ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \
     0
 
 /* Can we do the requested digest? */
 #define CHAP_CANDIGEST(mdtype, digest) \
     ((digest) == CHAP_DIGEST_MD5)? (mdtype) & MDTYPE_MD5: \
+    ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \
     ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \
     0
 
  *  Challenge lengths (for challenges we send) and other limits.
  */
 #define MIN_CHALLENGE_LENGTH   16
-#define MAX_CHALLENGE_LENGTH   24
+#define MAX_CHALLENGE_LENGTH   24      /* sufficient for MS-CHAP Peer Chal. */
 #define MAX_RESPONSE_LENGTH    64      /* sufficient for MD5 or MS-CHAP */
+#define MS_AUTH_RESPONSE_LENGTH        40      /* MS-CHAPv2 authenticator response, */
+                                       /* as ASCII */
 
 /*
  * Each interface is described by a chap structure.
@@ -114,6 +121,9 @@ typedef struct chap_state {
     int chal_transmits;                /* Number of transmissions of challenge */
     int resp_transmits;                /* Number of transmissions of response */
     u_char response[MAX_RESPONSE_LENGTH];      /* Response to send */
+    char saresponse[MS_AUTH_RESPONSE_LENGTH+1];        /* Auth response to send */
+    char earesponse[MS_AUTH_RESPONSE_LENGTH+1];        /* Auth response expected */
+                                               /* +1 for null terminator */
     u_char resp_length;                /* length of response */
     u_char resp_id;            /* ID for response messages */
     u_char resp_type;          /* hash algorithm for responses */
index c4b7d20298dbf18758b2bc391521c96abaef5dfa..d10b99a4f1980c6ac2f99fccd51d3bd2c202c9e2 100644 (file)
  *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
  */
 
-#define RCSID  "$Id: chap_ms.c,v 1.17 2002/03/04 14:59:51 dfs Exp $"
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ *   Implemented MS-CHAPv2 functionality.  Heavily based on
+ *   sample implementation in RFC 2759.
+ */
+
+#define RCSID  "$Id: chap_ms.c,v 1.18 2002/03/05 15:14:04 dfs Exp $"
 
 #ifdef CHAPMS
 
@@ -50,6 +57,7 @@
 #include "chap.h"
 #include "chap_ms.h"
 #include "md4.h"
+#include "sha1.h"
 
 #ifndef USE_CRYPT
 #include <des.h>
 static const char rcsid[] = RCSID;
 
 
-static void    ChallengeResponse __P((u_char *, u_char *, u_char *));
-static void    DesEncrypt __P((u_char *, u_char *, u_char *));
+static void    ChallengeHash __P((u_char[16], u_char *, char *, u_char[8]));
+static void    ascii2unicode __P((char[], int, u_char[]));
+static void    NTPasswordHash __P((char *, int, u_char[MD4_SIGNATURE_SIZE]));
+static void    ChallengeResponse __P((u_char *, u_char *, u_char[24]));
+static void    DesEncrypt __P((u_char *, u_char *, u_char[8]));
 static void    MakeKey __P((u_char *, u_char *));
 static u_char  Get7Bits __P((u_char *, int));
-static void    ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
+static void    ChapMS_NT __P((u_char *, char *, int, u_char[24]));
+static void    ChapMS2_NT __P((char *, u_char[16], char *, char *, int,
+                               u_char[24]));
+static void    GenerateAuthenticatorResponse __P((char*, int, u_char[24],
+                                                  u_char[16], u_char *,
+                                                  char *, u_char[41]));
 #ifdef MSLANMAN
-static void    ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
+static void    ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
 #endif
 
 #ifdef USE_CRYPT
@@ -72,30 +88,31 @@ static void Expand __P((u_char *, u_char *));
 static void    Collapse __P((u_char *, u_char *));
 #endif
 
+extern double drand48 __P((void));
+
 #ifdef MSLANMAN
 bool   ms_lanman = 0;          /* Use LanMan password instead of NT */
                                /* Has meaning only with MS-CHAP challenges */
 #endif
 
 static void
-ChallengeResponse(challenge, pwHash, response)
-    u_char *challenge; /* IN   8 octets */
-    u_char *pwHash;    /* IN  16 octets */
-    u_char *response;  /* OUT 24 octets */
+ChallengeResponse(u_char *challenge,
+                 u_char PasswordHash[MD4_SIGNATURE_SIZE],
+                 u_char response[24])
 {
     char    ZPasswordHash[21];
 
     BZERO(ZPasswordHash, sizeof(ZPasswordHash));
-    BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+    BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
 
 #if 0
     dbglog("ChallengeResponse - ZPasswordHash %.*B",
           sizeof(ZPasswordHash), ZPasswordHash);
 #endif
 
-    DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
-    DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
-    DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+    DesEncrypt(challenge, ZPasswordHash +  0, &response[0]);
+    DesEncrypt(challenge, ZPasswordHash +  7, &response[8]);
+    DesEncrypt(challenge, ZPasswordHash + 14, &response[16]);
 
 #if 0
     dbglog("ChallengeResponse - response %.24B", response);
@@ -105,10 +122,7 @@ ChallengeResponse(challenge, pwHash, response)
 
 #ifdef USE_CRYPT
 static void
-DesEncrypt(clear, key, cipher)
-    u_char *clear;     /* IN  8 octets */
-    u_char *key;       /* IN  7 octets */
-    u_char *cipher;    /* OUT 8 octets */
+DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
 {
     u_char des_key[8];
     u_char crypt_key[66];
@@ -135,10 +149,7 @@ DesEncrypt(clear, key, cipher)
 #else /* USE_CRYPT */
 
 static void
-DesEncrypt(clear, key, cipher)
-    u_char *clear;     /* IN  8 octets */
-    u_char *key;       /* IN  7 octets */
-    u_char *cipher;    /* OUT 8 octets */
+DesEncrypt(u_char *clear, u_char *key, u_char cipher[8])
 {
     des_cblock         des_key;
     des_key_schedule   key_schedule;
@@ -161,9 +172,7 @@ DesEncrypt(clear, key, cipher)
 #endif /* USE_CRYPT */
 
 
-static u_char Get7Bits(input, startBit)
-    u_char *input;
-    int startBit;
+static u_char Get7Bits(u_char *input, int startBit)
 {
     register unsigned int      word;
 
@@ -181,9 +190,7 @@ static u_char Get7Bits(input, startBit)
  * out == 64-byte string where each byte is either 1 or 0
  * Note that the low-order "bit" is always ignored by by setkey()
  */
-static void Expand(in, out)
-    u_char *in;
-    u_char *out;
+static void Expand(u_char *in, u_char *out)
 {
         int j, c;
         int i;
@@ -198,9 +205,7 @@ static void Expand(in, out)
 
 /* The inverse of Expand
  */
-static void Collapse(in, out)
-    u_char *in;
-    u_char *out;
+static void Collapse(u_char *in, u_char *out)
 {
         int j;
         int i;
@@ -215,9 +220,7 @@ static void Collapse(in, out)
 }
 #endif
 
-static void MakeKey(key, des_key)
-    u_char *key;       /* IN  56 bit DES key missing parity bits */
-    u_char *des_key;   /* OUT 64 bit DES key with parity bits added */
+static void MakeKey(u_char *key, u_char *des_key)
 {
     des_key[0] = Get7Bits(key,  0);
     des_key[1] = Get7Bits(key,  7);
@@ -238,49 +241,95 @@ static void MakeKey(key, des_key)
 #endif
 }
 
+
 static void
-ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
-    MS_ChapResponse    *response;
+ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
+             char *username, u_char Challenge[8])
+    
+{
+    SHA1_CTX   sha1Context;
+    u_char     sha1Hash[SHA1_SIGNATURE_SIZE];
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PeerChallenge, 16);
+    SHA1_Update(&sha1Context, rchallenge, 16);
+    SHA1_Update(&sha1Context, username, strlen(username));
+    SHA1_Final(sha1Hash, &sha1Context);
+
+    BCOPY(sha1Hash, Challenge, 8);
+}
+
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
+ * is machine-dependent.)
+ */
+static void
+ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
+{
+    int i;
+
+    BZERO(unicode, ascii_len * 2);
+    for (i = 0; i < ascii_len; i++)
+       unicode[i * 2] = (u_char) ascii[i];
+}
+
+static void
+NTPasswordHash(char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
 {
-    int                        i;
 #ifdef __NetBSD__
     /* NetBSD uses the libc md4 routines which take bytes instead of bits */
-    int                        mdlen = secret_len * 2;
+    int                        mdlen = secret_len;
 #else
-    int                        mdlen = secret_len * 2 * 8;
+    int                        mdlen = secret_len * 8;
 #endif
     MD4_CTX            md4Context;
-    u_char             hash[MD4_SIGNATURE_SIZE];
-    u_char             unicodePassword[MAX_NT_PASSWORD * 2];
-
-    /* Initialize the Unicode version of the secret (== password). */
-    /* This implicitly supports 8-bit ISO8859/1 characters. */
-    BZERO(unicodePassword, sizeof(unicodePassword));
-    for (i = 0; i < secret_len; i++)
-       unicodePassword[i * 2] = (u_char)secret[i];
 
     MD4Init(&md4Context);
-    MD4Update(&md4Context, unicodePassword, mdlen);
+    MD4Update(&md4Context, secret, mdlen);
+    MD4Final(hash, &md4Context);
+
+}
+
+static void
+ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
+         u_char NTResponse[24])
+{
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
 
-    MD4Final(hash, &md4Context);       /* Tell MD4 we're done */
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
 
-    ChallengeResponse(rchallenge, hash, response->NTResp);
+    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
+}
+
+static void
+ChapMS2_NT(char *rchallenge, u_char PeerChallenge[16], char *username,
+          char *secret, int secret_len, u_char NTResponse[24])
+{
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     Challenge[8];
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(Challenge, PasswordHash, NTResponse);
 }
 
 #ifdef MSLANMAN
 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
 
 static void
-ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
-    MS_ChapResponse    *response;
+ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+             u_char LMResponse[24])
 {
     int                        i;
     u_char             UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
@@ -292,39 +341,125 @@ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
        UcasePassword[i] = (u_char)toupper(secret[i]);
     DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
     DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
-    ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+    ChallengeResponse(rchallenge, PasswordHash, LMResponse);
 }
 #endif
 
-void
-ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
-    chap_state *cstate;
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
+
+static void
+GenerateAuthenticatorResponse(char *secret, int secret_len,
+                             u_char NTResponse[24], u_char PeerChallenge[16],
+                             u_char *rchallenge, char *username,
+                             u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
 {
-    MS_ChapResponse    response;
+    /*
+     * "Magic" constants used in response generation, from RFC 2759.
+     */
+    u_char Magic1[39] = /* "Magic server to client signing constant" */
+       {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+        0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+        0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+        0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
+    u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+       {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+        0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+        0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+        0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+        0x6E};
+
+    int                i;
+    SHA1_CTX   sha1Context;
+    u_char     unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char     PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char     PasswordHashHash[MD4_SIGNATURE_SIZE];
+    u_char     Digest[SHA1_SIGNATURE_SIZE];
+    u_char     Challenge[8];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, sizeof(PasswordHashHash));
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(Digest, &sha1Context);
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, Digest, sizeof(Digest));
+    SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
+    SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Convert to ASCII hex string. */
+    for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
+       sprintf(&authResponse[i * 2], "%02X", Digest[i]);
+}
 
+
+void
+ChapMS(chap_state *cstate, u_char *rchallenge, char *secret, int secret_len,
+       MS_ChapResponse *response)
+{
 #if 0
     CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
 #endif
-    BZERO(&response, sizeof(response));
+    BZERO(response, sizeof(response));
 
     /* Calculate both always */
-    ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+    ChapMS_NT(rchallenge, secret, secret_len, response->NTResp);
 
 #ifdef MSLANMAN
-    ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+    ChapMS_LANMan(rchallenge, secret, secret_len, response);
 
     /* prefered method is set by option  */
-    response.UseNT[0] = !ms_lanman;
+    response->UseNT[0] = !ms_lanman;
 #else
-    response.UseNT[0] = 1;
+    response->UseNT[0] = 1;
 #endif
 
-    BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
-    cstate->resp_length = MS_CHAP_RESPONSE_LEN;
 }
 
+
+/*
+ * If PeerChallenge is NULL, one is generated and response->PeerChallenge
+ * is filled in.  Call this way when generating a response.
+ * If PeerChallenge is supplied, it is copied into response->PeerChallenge.
+ * Call this way when verifying a response.
+ *
+ * response->PeerChallenge is then used for calculation of the
+ * Authenticator Response.
+ */
+void
+ChapMS2(chap_state *cstate, u_char *rchallenge, u_char *PeerChallenge,
+       char *user, char *secret, int secret_len, MS_Chap2Response *response,
+       u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    u_char *p = response->PeerChallenge;
+    int i;
+
+    BZERO(response, sizeof(response));
+
+    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+    if (!PeerChallenge)
+       for (i = 0; i < sizeof(response->PeerChallenge); i++)
+           *p++ = (u_char) (drand48() * 0xff);
+    else
+       BCOPY(PeerChallenge, response->PeerChallenge,
+             sizeof(response->PeerChallenge));
+
+    /* Generate the NT-Response */
+    ChapMS2_NT(rchallenge, response->PeerChallenge, user,
+              secret, secret_len, response->NTResp);
+
+    /* Generate the Authenticator Response. */
+    GenerateAuthenticatorResponse(secret, secret_len, response->NTResp,
+                                 response->PeerChallenge, rchallenge,
+                                 user, authResponse);
+}
+
+
 #endif /* CHAPMS */
index 4418c9305f160e7b9c8c45f403c9e5a3eb1692ae..a8a13950ab5f3448c2392e0a2939a2ba2bf40235 100644 (file)
@@ -19,7 +19,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: chap_ms.h,v 1.4 2002/03/04 14:59:51 dfs Exp $
+ * $Id: chap_ms.h,v 1.5 2002/03/05 15:14:04 dfs Exp $
  */
 
 #ifndef __CHAPMS_INCLUDE__
 #define MAX_NT_PASSWORD                256     /* Max (Unicode) chars in an NT pass */
 
 #define MS_CHAP_RESPONSE_LEN   49      /* Response length for MS-CHAP */
+#define MS_CHAP2_RESPONSE_LEN  49      /* Response length for MS-CHAPv2 */
+
+/*
+ * E=eeeeeeeeee error codes for MS-CHAP failure messages. 
+ * Offset by 646 (the minimum code) for switch() handling on older compilers.
+ */
+#define MS_CHAP_ERROR_BASE                     646
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS   (646 - MS_CHAP_ERROR_BASE)
+#define MS_CHAP_ERROR_ACCT_DISABLED            (647 - MS_CHAP_ERROR_BASE)
+#define MS_CHAP_ERROR_PASSWD_EXPIRED           (648 - MS_CHAP_ERROR_BASE)
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION     (649 - MS_CHAP_ERROR_BASE)
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE   (691 - MS_CHAP_ERROR_BASE)
+#define MS_CHAP_ERROR_CHANGING_PASSWORD                (709 - MS_CHAP_ERROR_BASE)
 
 /*
  * Use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
@@ -39,7 +52,20 @@ typedef struct {
     u_char UseNT[1];           /* If 1, ignore the LANMan response field */
 } MS_ChapResponse;
 
-void ChapMS __P((chap_state *, char *, int, char *, int));
+/*
+ * Use MS_CHAP2_RESPONSE_LEN, rather than sizeof(MS_Chap2Response),
+ * in case this struct gets padded.
+ */
+typedef struct {
+    u_char PeerChallenge[16];
+    u_char Reserved[8];                /* Must be zero */
+    u_char NTResp[24];
+    u_char Flags[1];           /* Must be zero */
+} MS_Chap2Response;
+
+void ChapMS __P((chap_state *, u_char *, char *, int, MS_ChapResponse *));
+void ChapMS2 __P((chap_state *, u_char *, u_char *, char *, char *, int,
+                 MS_Chap2Response *, u_char[MS_AUTH_RESPONSE_LENGTH+1]));
 
 #define __CHAPMS_INCLUDE__
 #endif /* __CHAPMS_INCLUDE__ */
index 8cf6029c8ec4ad586674912f4832bea09b8f512a..050ad201d3f3c37c6b844b8c81b67a2c04029f6e 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: lcp.c,v 1.58 2002/03/01 14:39:18 dfs Exp $"
+#define RCSID  "$Id: lcp.c,v 1.60 2002/04/02 13:54:59 dfs Exp $"
 
 /*
  * TODO:
@@ -1943,7 +1943,12 @@ lcp_printpkt(p, plen, printer, arg)
                                break;
 #ifdef CHAPMS
                            case CHAP_MICROSOFT:
-                               printer(arg, " m$oft");
+                               printer(arg, " MS");
+                               ++p;
+                               break;
+
+                           case CHAP_MICROSOFT_V2:
+                               printer(arg, " MS-v2");
                                ++p;
                                break;
 #endif
index 5e9bc1aea4009f402cb20dcf62eb13067c126fff..516c96d14cd8411aa031810367ee3e0cd24bb423 100644 (file)
@@ -1,5 +1,5 @@
 .\" manual page [] for RADIUS plugin for pppd 2.4
-.\" $Id: pppd-radius.8,v 1.2 2002/03/04 14:59:51 dfs Exp $
+.\" $Id: pppd-radius.8,v 1.4 2002/04/02 13:55:00 dfs Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -17,8 +17,8 @@ radius.so \- RADIUS authentication plugin for
 plugin radius.so
 .SH DESCRIPTION
 .LP
-The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, and MS-CHAP
-authentication against a RADIUS server instead of the usual
+The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, MS-CHAP and
+MS-CHAPv2 authentication against a RADIUS server instead of the usual
 .I /etc/ppp/pap-secrets
 and
 .I /etc/ppp/chap-secrets
index 358fc95a654c733dcd704b9bb7857a1073fdbb41..a36c34311bb0812d840a76753b07c91bfd9c354a 100644 (file)
@@ -2,8 +2,8 @@
 *
 * radius.c
 *
-* RADIUS plugin for pppd.  Performs PAP, CHAP and MS-CHAP authentication
-* using RADIUS.
+* RADIUS plugin for pppd.  Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
+* authentication using RADIUS.
 *
 * Copyright (C) 2002 Roaring Penguin Software Inc.
 *
@@ -21,7 +21,7 @@
 *
 ***********************************************************************/
 static char const RCSID[] =
-"$Id: radius.c,v 1.5 2002/03/04 14:59:51 dfs Exp $";
+"$Id: radius.c,v 1.6 2002/03/05 15:14:05 dfs Exp $";
 
 #include "pppd.h"
 #include "chap.h"
@@ -58,7 +58,7 @@ static int radius_chap_auth(char *user,
 static void radius_ip_up(void *opaque, int arg);
 static void radius_ip_down(void *opaque, int arg);
 static void make_username_realm(char *user);
-static int radius_setparams(VALUE_PAIR *vp, char *msg);
+static int radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg);
 static void radius_choose_ip(u_int32_t *addrp);
 static int radius_init(char *msg);
 static int get_client_port(char *ifname);
@@ -233,7 +233,7 @@ radius_pap_auth(char *user,
     }
 
     if (result == OK_RC) {
-       if (radius_setparams(received, radius_msg) < 0) {
+       if (radius_setparams(NULL, received, radius_msg) < 0) {
            result = ERROR_RC;
        }
     }
@@ -255,7 +255,7 @@ radius_pap_auth(char *user,
 * %RETURNS:
 *  CHAP_SUCCESS if we can authenticate, CHAP_FAILURE if we cannot.
 * %DESCRIPTION:
-* Performs CHAP and MS-CHAP authentication using RADIUS
+* Performs CHAP, MS-CHAP and MS-CHAPv2 authentication using RADIUS
 ***********************************************************************/
 static int
 radius_chap_auth(char *user,
@@ -279,6 +279,7 @@ radius_chap_auth(char *user,
     if ((cstate->chal_type != CHAP_DIGEST_MD5)
 #ifdef CHAPMS
        && (cstate->chal_type != CHAP_MICROSOFT)
+       && (cstate->chal_type != CHAP_MICROSOFT_V2)
 #endif
        ) {
        error("RADIUS: Challenge type %u unsupported", cstate->chal_type);
@@ -325,7 +326,7 @@ radius_chap_auth(char *user,
     case CHAP_MICROSOFT:
     {
        /* MS-CHAP-Challenge and MS-CHAP-Response */
-       MS_ChapResponse *rmd = remmd;
+       MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
        u_char *p = cpassword;
 
        *p++ = cstate->chal_id;
@@ -342,6 +343,29 @@ radius_chap_auth(char *user,
                      cpassword, MS_CHAP_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
        break;
     }
+
+    case CHAP_MICROSOFT_V2:
+    {
+       /* MS-CHAP-Challenge and MS-CHAP2-Response */
+       MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
+       u_char *p = cpassword;
+
+       *p++ = cstate->chal_id;
+       /* The idiots use a different field order in RADIUS than PPP */
+       memcpy(p, rmd->Flags, sizeof(rmd->Flags));
+       p += sizeof(rmd->Flags);
+       memcpy(p, rmd->PeerChallenge, sizeof(rmd->PeerChallenge));
+       p += sizeof(rmd->PeerChallenge);
+       memcpy(p, rmd->Reserved, sizeof(rmd->Reserved));
+       p += sizeof(rmd->Reserved);
+       memcpy(p, rmd->NTResp, sizeof(rmd->NTResp));
+
+       rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE,
+                     cstate->challenge, cstate->chal_len, VENDOR_MICROSOFT);
+       rc_avpair_add(&send, PW_MS_CHAP2_RESPONSE,
+                     cpassword, MS_CHAP2_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
+       break;
+    }
 #endif
 
     }
@@ -360,7 +384,7 @@ radius_chap_auth(char *user,
 
     if (result == OK_RC) {
        if (!rstate.done_chap_once) {
-           if (radius_setparams(received, radius_msg) < 0) {
+           if (radius_setparams(cstate, received, radius_msg) < 0) {
                error("%s", radius_msg);
                result = ERROR_RC;
            } else {
@@ -408,18 +432,20 @@ make_username_realm(char *user)
 /**********************************************************************
 * %FUNCTION: radius_setparams
 * %ARGUMENTS:
+*  cstate -- pppd's chap_state structure
 *  vp -- received value-pairs
 *  msg -- buffer in which to place error message.  Holds up to BUF_LEN chars
 * %RETURNS:
 *  >= 0 on success; -1 on failure
 * %DESCRIPTION:
 *  Parses attributes sent by RADIUS server and sets them in pppd.  Currently,
-*  used only to set IP address.
+*  used only to set IP address and MS-CHAPv2 Authenticator Response.
 ***********************************************************************/
 static int
-radius_setparams(VALUE_PAIR *vp, char *msg)
+radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg)
 {
     u_int32_t remote;
+    int ms_chap2_success = 0;
 
     /* Send RADIUS attributes to anyone else who might be interested */
     if (radius_attributes_hook) {
@@ -432,49 +458,62 @@ radius_setparams(VALUE_PAIR *vp, char *msg)
      */
 
     while (vp) {
-       if (vp->vendorcode == VENDOR_NONE) {
-           switch (vp->attribute) {
-           case PW_SERVICE_TYPE:
-               /* check for service type       */
-               /* if not FRAMED then exit      */
-               if (vp->lvalue != PW_FRAMED) {
-                   slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
-                            vp->lvalue, rstate.user);
-                   return -1;
-               }
-               break;
-           case PW_FRAMED_PROTOCOL:
-               /* check for framed protocol type       */
-               /* if not PPP then also exit            */
-               if (vp->lvalue != PW_PPP) {
-                   slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
-                            vp->lvalue, rstate.user);
+       if ((vp->attribute == PW_SERVICE_TYPE) &&
+           (vp->vendorcode == VENDOR_NONE)) {
+           /* check for service type       */
+           /* if not FRAMED then exit      */
+           if (vp->lvalue != PW_FRAMED) {
+               slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
+                        vp->lvalue, rstate.user);
+               return -1;
+           }
+       } else if ((vp->attribute == PW_FRAMED_PROTOCOL) &&
+                  (vp->vendorcode == VENDOR_NONE)) {
+           /* check for framed protocol type       */
+           /* if not PPP then also exit            */
+           if (vp->lvalue != PW_PPP) {
+               slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
+                        vp->lvalue, rstate.user);
+               return -1;
+           }
+       } else if ((vp->attribute == PW_FRAMED_IP_ADDRESS) &&
+                  (vp->vendorcode == VENDOR_NONE)) {
+           /* seting up remote IP addresses */
+           remote = vp->lvalue;
+           if (remote == 0xffffffff) {
+               /* 0xffffffff means user should be allowed to select one */
+               rstate.any_ip_addr_ok = 1;
+           } else if (remote != 0xfffffffe) {
+               /* 0xfffffffe means NAS should select an ip address */
+               remote = htonl(vp->lvalue);
+               if (bad_ip_adrs (remote)) {
+                   slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
+                            remote, rstate.user);
                    return -1;
                }
-               break;
-
-           case PW_FRAMED_IP_ADDRESS:
-               /* seting up remote IP addresses */
-               remote = vp->lvalue;
-               if (remote == 0xffffffff) {
-                   /* 0xffffffff means user should be allowed to select one */
-                   rstate.any_ip_addr_ok = 1;
-               } else if (remote != 0xfffffffe) {
-                   /* 0xfffffffe means NAS should select an ip address */
-                   remote = htonl(vp->lvalue);
-                   if (bad_ip_adrs (remote)) {
-                       slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
-                                remote, rstate.user);
-                       return -1;
-                   }
-                   rstate.choose_ip = 1;
-                   rstate.ip_addr = remote;
-               }
-           break;
+               rstate.choose_ip = 1;
+               rstate.ip_addr = remote;
            }
+#ifdef CHAPMS
+       } else if ((vp->attribute == PW_MS_CHAP2_SUCCESS) &&
+                  (vp->vendorcode == VENDOR_MICROSOFT)) {
+           if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) {
+               slprintf(msg, BUF_LEN, "RADIUS: bad MS-CHAP2-Success packet");
+               return -1;
+           }
+           memcpy(cstate->saresponse, vp->strvalue + 3,
+                  MS_AUTH_RESPONSE_LENGTH);
+           cstate->saresponse[MS_AUTH_RESPONSE_LENGTH] = '\0';
+           ms_chap2_success = 1;
+#endif /* CHAPMS */
        }
        vp = vp->next;
     }
+
+    /* Require a valid MS-CHAP2-SUCCESS for MS-CHAPv2 auth */
+    if (cstate && (cstate->chal_type == CHAP_MICROSOFT_V2) && !ms_chap2_success)
+       return -1;
+
     return 0;
 }
 
index 7b3e7b08ef8928e2746a58f23a11915138ccf71a..b8d77719c5814bbe7c90a9a7d55eb03ece5fe9ad 100644 (file)
@@ -228,3 +228,4 @@ VALUE               Add-Port-To-IP-Address  Yes                     1
 #VALUE         Server-Config           Password-Expiration     30
 #VALUE         Server-Config           Password-Warning        5
 
+INCLUDE /etc/radiusclient/dictionary.microsoft
index 141e69039ac16ff88c6259fac91c2d2912ecbd8a..18106a663b025247cb77ad88de23fd75ee5c8e4d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: radiusclient.h,v 1.4 2002/03/04 14:59:52 dfs Exp $
+ * $Id: radiusclient.h,v 1.5 2002/03/05 15:14:06 dfs Exp $
  *
  * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg
  *
@@ -157,6 +157,8 @@ typedef struct pw_auth_hdr
 /* Vendor RADIUS attribute-value pairs */
 #define PW_MS_CHAP_CHALLENGE           11      /* string */
 #define PW_MS_CHAP_RESPONSE            1       /* string */
+#define PW_MS_CHAP2_RESPONSE           25      /* string */
+#define PW_MS_CHAP2_SUCCESS            26      /* string */
 
 /*     Accounting */
 
index 95a0838abd13fb9082893534614bc1e891d1754b..a619630ffa4f0446ea2a1b0edd96284e71032702 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: dict.c,v 1.1 2002/01/22 16:03:02 dfs Exp $
+ * $Id: dict.c,v 1.2 2002/03/05 15:14:06 dfs Exp $
  *
  * Copyright (C) 2002 Roaring Penguin Software Inc.
  *
@@ -51,7 +51,7 @@ int rc_read_dictionary (char *filename)
        int             value;
        int             type;
        int             n;
-
+       int             retcode;
        if ((dictfd = fopen (filename, "r")) == (FILE *) NULL)
        {
                rc_log(LOG_ERR, "rc_read_dictionary: couldn't open dictionary %s: %s",
@@ -60,6 +60,7 @@ int rc_read_dictionary (char *filename)
        }
 
        line_no = 0;
+       retcode = 0;
        while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL)
        {
                line_no++;
@@ -75,19 +76,22 @@ int rc_read_dictionary (char *filename)
                    if (sscanf(buffer, "%s%s%d", dummystr, namestr, &value) != 3) {
                        rc_log(LOG_ERR, "rc_read_dictionary: invalid vendor on line %d of dictionary %s",
                               line_no, filename);
-                       return (-1);
+                       retcode = -1;
+                       break;
                    }
                    /* Validate entry */
                    if (strlen (namestr) > NAME_LENGTH) {
                        rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s",
                               line_no, filename);
-                       return (-1);
+                       retcode = -1;
+                       break;
                    }
                    /* Create new vendor entry */
                    vdict = (VENDOR_DICT *) malloc (sizeof (VENDOR_DICT));
                    if (!vdict) {
                        rc_log(LOG_CRIT, "rc_read_dictionary: out of memory");
-                       return (-1);
+                       retcode = -1;
+                       break;
                    }
                    strcpy(vdict->vendorname, namestr);
                    vdict->vendorcode = value;
@@ -107,7 +111,8 @@ int rc_read_dictionary (char *filename)
                        {
                                rc_log(LOG_ERR, "rc_read_dictionary: invalid attribute on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        /*
@@ -117,14 +122,16 @@ int rc_read_dictionary (char *filename)
                        {
                                rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        if (strlen (vendorstr) > NAME_LENGTH)
                        {
                                rc_log(LOG_ERR, "rc_read_dictionary: invalid name length on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        if (!isdigit (*valstr))
@@ -132,7 +139,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                                 "rc_read_dictionary: invalid value on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
                        value = atoi (valstr);
 
@@ -157,7 +165,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                                  "rc_read_dictionary: invalid type on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        /* Search for vendor if supplied */
@@ -167,7 +176,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                                       "rc_read_dictionary: unknown vendor on line %d of dictionary %s",
                                       line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                            }
                        } else {
                            vdict = NULL;
@@ -178,7 +188,8 @@ int rc_read_dictionary (char *filename)
                                                        == (DICT_ATTR *) NULL)
                        {
                                rc_log(LOG_CRIT, "rc_read_dictionary: out of memory");
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
                        strcpy (attr->name, namestr);
                        if (vdict) {
@@ -207,7 +218,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                           "rc_read_dictionary: invalid value entry on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        /*
@@ -218,7 +230,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                      "rc_read_dictionary: invalid attribute length on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        if (strlen (namestr) > NAME_LENGTH)
@@ -226,7 +239,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                           "rc_read_dictionary: invalid name length on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
 
                        if (!isdigit (*valstr))
@@ -234,7 +248,8 @@ int rc_read_dictionary (char *filename)
                                rc_log(LOG_ERR,
                                 "rc_read_dictionary: invalid value on line %d of dictionary %s",
                                         line_no, filename);
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
                        value = atoi (valstr);
 
@@ -244,7 +259,8 @@ int rc_read_dictionary (char *filename)
                                                        == (DICT_VALUE *) NULL)
                        {
                                rc_log(LOG_CRIT, "rc_read_dictionary: out of memory");
-                               return (-1);
+                               retcode = -1;
+                               break;
                        }
                        strcpy (dval->attrname, attrstr);
                        strcpy (dval->name, namestr);
@@ -254,9 +270,26 @@ int rc_read_dictionary (char *filename)
                        dval->next = dictionary_values;
                        dictionary_values = dval;
                }
+               else if (strncmp (buffer, "INCLUDE", 7) == 0)
+               {
+                       /* Read the INCLUDE line */
+                       if (sscanf (buffer, "%s%s", dummystr, namestr) != 2)
+                       {
+                               rc_log(LOG_ERR,
+                          "rc_read_dictionary: invalid include entry on line %d of dictionary %s",
+                                        line_no, filename);
+                               retcode = -1;
+                               break;
+                       }
+                       if (rc_read_dictionary(namestr) == -1)
+                       {
+                               retcode = -1;
+                               break;
+                       }
+               }
        }
        fclose (dictfd);
-       return (0);
+       return retcode;
 }
 
 /*
index 229708a12b1cfa5947cb6af24e00cae18f283a73..f6adc5daf2767ffff5ffe651aec596e33accab36 100644 (file)
@@ -1,5 +1,5 @@
 .\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.60 2002/03/01 14:39:18 dfs Exp $
+.\" $Id: pppd.8,v 1.61 2002/03/05 15:14:04 dfs Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -857,6 +857,10 @@ peer using CHAP.
 With this option, pppd will not agree to authenticate itself to the
 peer using MS-CHAP.
 .TP
+.B refuse-mschap-v2
+With this option, pppd will not agree to authenticate itself to the
+peer using MS-CHAPv2.
+.TP
 .B refuse-pap
 With this option, pppd will not agree to authenticate itself to the
 peer using PAP.
@@ -869,6 +873,10 @@ Handshake Authentication Protocol] authentication.
 Require the peer to authenticate itself using MS-CHAP [Microsft Challenge
 Handshake Authentication Protocol] authentication.
 .TP
+.B require-mschap-v2
+Require the peer to authenticate itself using MS-CHAPv2 [Microsft Challenge
+Handshake Authentication Protocol, Version 2] authentication.
+.TP
 .B require-pap
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
@@ -1020,7 +1028,8 @@ pppd will not agree to authenticate itself with a particular protocol
 if it has no secrets which could be used to do so.
 .LP
 Pppd stores secrets for use in authentication in secrets
-files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP/MS-CHAP).
+files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for
+CHAP/MS-CHAP/MS-CHAPv2).
 Both secrets files have the same format.  The secrets files can
 contain secrets for pppd to use in authenticating itself to other
 systems, as well as secrets for pppd to use when authenticating other
@@ -1498,7 +1507,8 @@ script.
 .B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others)
 Process-ID for pppd process on ppp interface unit \fIn\fR.
 .TP
-.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp-\fIname\fB.pid \fR(others)
+.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux),
+\fB/etc/ppp/ppp-\fIname\fB.pid \fR(others)
 Process-ID for pppd process for logical link \fIname\fR (see the
 \fIlinkname\fR option).
 .TP
@@ -1508,8 +1518,8 @@ file should be owned by root and not readable or writable by any other
 user.  Pppd will log a warning if this is not the case.
 .TP
 .B /etc/ppp/chap-secrets
-Names, secrets and IP addresses for CHAP/MS-CHAP authentication.  As for
-/etc/ppp/pap-secrets, this file should be owned by root and not
+Names, secrets and IP addresses for CHAP/MS-CHAP/MS-CHAPv2 authentication.
+As for /etc/ppp/pap-secrets, this file should be owned by root and not
 readable or writable by any other user.  Pppd will log a warning if
 this is not the case.
 .TP
diff --git a/pppd/sha1.c b/pppd/sha1.c
new file mode 100644 (file)
index 0000000..b56a7c9
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
+ * 
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ * 
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+#include <netinet/in.h>        /* htonl() */
+#include "sha1.h"
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0(i) (block->l[i] = htonl(block->l[i]))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1_Transform(unsigned long state[5], const unsigned char buffer[64])
+{
+    unsigned long a, b, c, d, e;
+    typedef union {
+       unsigned char c[64];
+       unsigned long l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16 *block;
+
+#ifdef SHA1HANDSOFF
+    static unsigned char workspace[64];
+    block = (CHAR64LONG16 *) workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16 *) buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1_Init(SHA1_CTX *context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
+{
+    unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+       memcpy(&context->buffer[j], data, (i = 64-j));
+       SHA1_Transform(context->state, context->buffer);
+       for ( ; i + 63 < len; i += 64) {
+           SHA1_Transform(context->state, &data[i]);
+       }
+       j = 0;
+    }
+    else
+       i = 0;
+
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
+{
+    unsigned long i, j;
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *) "\200", 1);
+    while ((context->count[0] & 504) != 448) {
+       SHA1_Update(context, (unsigned char *) "\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+    for (i = 0; i < 20; i++) {
+       digest[i] = (unsigned char)
+                    ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+
diff --git a/pppd/sha1.h b/pppd/sha1.h
new file mode 100644 (file)
index 0000000..6727afb
--- /dev/null
@@ -0,0 +1,19 @@
+/* sha1.h */
+
+#ifndef __SHA1_INCLUDE_
+
+typedef struct {
+    unsigned long state[5];
+    unsigned long count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+#define SHA1_SIGNATURE_SIZE 20
+
+void SHA1_Transform(unsigned long[5], const unsigned char[64]);
+void SHA1_Init(SHA1_CTX *);
+void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
+void SHA1_Final(unsigned char[20], SHA1_CTX *);
+
+#define __SHA1_INCLUDE_
+#endif /* __SHA1_INCLUDE_ */
index 935f56272a87e8ea55db72265095d4011e0d5e97..38a69778a270e926d17937d8a7609de8dcacb730 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: utils.c,v 1.17 2002/01/14 15:31:34 dfs Exp $"
+#define RCSID  "$Id: utils.c,v 1.18 2002/03/05 15:14:04 dfs Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -209,6 +209,28 @@ vslprintf(buf, buflen, fmt, args)
        neg = 0;
        ++fmt;
        switch (c) {
+       case 'l':
+           c = *fmt++;
+           switch (c) {
+           case 'd':
+               val = va_arg(args, long);
+               if (val < 0) {
+                   neg = 1;
+                   val = -val;
+               }
+               base = 10;
+               break;
+           case 'u':
+               val = va_arg(args, unsigned long);
+               base = 10;
+               break;
+           default:
+               *buf++ = '%'; --buflen;
+               *buf++ = 'l'; --buflen;
+               --fmt;          /* so %lz outputs %lz etc. */
+               continue;
+           }
+           break;
        case 'd':
            i = va_arg(args, int);
            if (i < 0) {