$NetBSD: patch-bc,v 1.1.1.1 1999/10/08 04:34:43 dbj Exp $

--- pppd/cbcp.c.orig2	Sat Sep 25 12:51:36 1999
+++ pppd/cbcp.c	Sat Sep 25 13:11:31 1999
@@ -17,4 +17,25 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
+
+/*
+ *               Microsoft Call Back Configuration Protocol.
+ *                         by Pedro Roque Marques
+ * 
+ * The CBCP is a method by which the Microsoft Windows NT Server may
+ * implement additional security. It is possible to configure the server
+ * in such a manner so as to require that the client systems which
+ * connect with it are required that following a valid authentication to
+ * leave a method by which the number may be returned call.
+ * 
+ * It is a requirement of servers so configured that the protocol be
+ * exchanged.
+ * 
+ * So, this set of patches may be applied to the pppd process to enable
+ * the cbcp client *only* portion of the specification. It is primarily
+ * meant to permit connection with Windows NT Servers.
+ * 
+ * The ietf-working specification may be obtained from ftp.microsoft.com
+ * in the developr/rfc directory.
+ */
 
@@ -32,14 +52,25 @@
 
 static const char rcsid[] = RCSID;
 
+void network_phase __P((int));
+
 /*
  * Options.
  */
 static int setcbcp __P((char **));
+static int setcbcpreq __P((char **));
+static int setnocbcp __P((char **));
 
 static option_t cbcp_option_list[] = {
     { "callback", o_special, setcbcp,
       "Ask for callback" },
+    { "+callback", o_special_noarg, setcbcpreq,
+      "Ask for callback" },
+    { "nocallback", o_special, setnocbcp,
+      "Don't allow callbacks" },
+    { "-callback", o_special, setnocbcp,
+      "Don't allow callbacks" },
+
     { NULL }
 };
 
@@ -48,7 +79,9 @@
  */
 static void cbcp_init      __P((int unit));
 static void cbcp_open      __P((int unit));
+static void cbcp_close     __P((int unit, char *));	
 static void cbcp_lowerup   __P((int unit));
+static void cbcp_lowerdown __P((int unit));
 static void cbcp_input     __P((int unit, u_char *pkt, int len));
 static void cbcp_protrej   __P((int unit));
 static int  cbcp_printpkt  __P((u_char *pkt, int len,
@@ -61,12 +94,12 @@
     cbcp_input,
     cbcp_protrej,
     cbcp_lowerup,
-    NULL,
+    cbcp_lowerdown,
     cbcp_open,
-    NULL,
+    cbcp_close,
     cbcp_printpkt,
     NULL,
-    0,
+    1,
     "CBCP",
     NULL,
     cbcp_option_list,
@@ -80,23 +113,46 @@
 /* internal prototypes */
 
 static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
-static void cbcp_resp __P((cbcp_state *us));
-static void cbcp_up __P((cbcp_state *us));
 static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_recvresp __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_req __P((cbcp_state *us));
+static void cbcp_ack __P((cbcp_state *us));
 static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+static void cbcp_up __P((cbcp_state *us));
 
 /* option processing */
 static int
 setcbcp(argv)
     char **argv;
 {
-    lcp_wantoptions[0].neg_cbcp = 1;
-    cbcp_protent.enabled_flag = 1;
+    lcp_allowoptions[0].neg_cbcp = 1;
     cbcp[0].us_number = strdup(*argv);
     if (cbcp[0].us_number == 0)
 	novm("callback number");
-    cbcp[0].us_type |= (1 << CB_CONF_USER);
-    cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+    if (cbcp[0].us_number[0] == '-')
+	cbcp[0].us_type = (1 << CB_CONF_NO);
+    else
+    {
+	cbcp[0].us_type = (1 << CB_CONF_USER);
+	cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+    }
+    return (1);
+}
+
+static int
+setnocbcp(argv)
+    char **argv;
+{
+    lcp_allowoptions[0].neg_cbcp = lcp_wantoptions[0].neg_cbcp = 0;
+    return (1);
+}
+
+static int
+setcbcpreq(argv)
+    char **argv;
+{
+    lcp_wantoptions[0].neg_cbcp = 1;
     return (1);
 }
 
@@ -110,7 +166,8 @@
     us = &cbcp[iface];
     memset(us, 0, sizeof(cbcp_state));
     us->us_unit = iface;
-    us->us_type |= (1 << CB_CONF_NO);
+    us->us_type = (1 << CB_CONF_NO);
+    us->us_id = 1;
 }
 
 /* lower layer is up */
@@ -120,18 +177,60 @@
 {
     cbcp_state *us = &cbcp[iface];
 
-    dbglog("cbcp_lowerup");
-    dbglog("want: %d", us->us_type);
+    if (debug)
+    {
+        dbglog("cbcp_lowerup");
+        dbglog("want: %d", us->us_type);
 
-    if (us->us_type == CB_CONF_USER)
-        dbglog("phone no: %s", us->us_number);
+        if (us->us_type & (1 << CB_CONF_USER))
+            dbglog("phone no: %s", us->us_number);
+    }
+}
+
+static void
+cbcp_lowerdown(iface)
+    int iface;
+{
+    if(debug)
+        dbglog("cbcp_lowerdown");
 }
 
 static void
 cbcp_open(unit)
     int unit;
 {
-    dbglog("cbcp_open");
+    lcp_options *ho = &lcp_hisoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *go = &lcp_gotoptions[unit];
+    cbcp_state *us = &cbcp[unit];
+
+    if(debug)
+        dbglog("cbcp_open");
+    if(ao->neg_cbcp)
+    {
+        if(ho->neg_cbcp)
+        {
+           cbcp_req(us);
+           return;
+        }
+    }
+    else if(wo->neg_cbcp)
+    {
+       if(!go->neg_cbcp)
+           lcp_close(0, "Callback required");
+       return;
+    }
+    cbcp_up(us);
+}
+
+static void
+cbcp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    if(debug)
+        dbglog("cbcp_close: %s", reason);
 }
 
 /* process an incomming packet */
@@ -174,11 +273,13 @@
 	break;
 
     case CBCP_RESP:
-	dbglog("CBCP_RESP received");
+        if (id != us->us_id && debug)
+            dbglog("id doesn't match: expected %d recv %d", us->us_id, id);
+        cbcp_recvresp(us, inp, len);
 	break;
 
     case CBCP_ACK:
-	if (id != us->us_id)
+	if (id != us->us_id && debug)
 	    dbglog("id doesn't match: expected %d recv %d",
 		   us->us_id, id);
 
@@ -298,7 +399,8 @@
     address[0] = 0;
 
     while (len) {
-        dbglog("length: %d", len);
+        if (debug)
+            dbglog("length: %d", len);
 
 	GETCHAR(type, pckt);
 	GETCHAR(opt_len, pckt);
@@ -310,22 +412,22 @@
 
 	switch(type) {
 	case CB_CONF_NO:
-	    dbglog("no callback allowed");
+	    dbglog("Callback: none");
 	    break;
 
 	case CB_CONF_USER:
-	    dbglog("user callback allowed");
 	    if (opt_len > 4) {
 	        GETCHAR(addr_type, pckt);
 		memcpy(address, pckt, opt_len - 4);
 		address[opt_len - 4] = 0;
-		if (address[0])
-		    dbglog("address: %s", address);
+	        dbglog("Callback: user callback, address: '%s'", address);
 	    }
+            else
+                dbglog("Callback: user callback");
 	    break;
 
 	case CB_CONF_ADMIN:
-	    dbglog("user admin defined allowed");
+	    dbglog("Callback: user admin defined");
 	    break;
 
 	case CB_CONF_LIST:
@@ -347,29 +449,35 @@
     int len = 0;
 
     cb_type = us->us_allowed & us->us_type;
-    dbglog("cbcp_resp cb_type=%d", cb_type);
+    if (debug)
+        dbglog("cbcp_resp cb_type=%d", cb_type);
 
+    if (!cb_type) {
+        dbglog("Your remote side wanted a callback-type you don't allow -> doing no callback");
+        cb_type = 1 << CB_CONF_NO;
 #if 0
-    if (!cb_type)
         lcp_down(us->us_unit);
 #endif
+    }
 
     if (cb_type & ( 1 << CB_CONF_USER ) ) {
-	dbglog("cbcp_resp CONF_USER");
+        if (debug)
+	    dbglog("cbcp_resp CONF_USER");
 	PUTCHAR(CB_CONF_USER, bufp);
-	len = 3 + 1 + strlen(us->us_number) + 1;
+	len = 2 + 1 + strlen(us->us_number);
 	PUTCHAR(len , bufp);
 	PUTCHAR(5, bufp); /* delay */
 	PUTCHAR(1, bufp);
-	BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+	BCOPY(us->us_number, bufp, strlen(us->us_number));
 	cbcp_send(us, CBCP_RESP, buf, len);
 	return;
     }
 
     if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
-	dbglog("cbcp_resp CONF_ADMIN");
+	if (debug)
+            dbglog("cbcp_resp CONF_ADMIN");
         PUTCHAR(CB_CONF_ADMIN, bufp);
-	len = 3;
+	len = 2 + 1;
 	PUTCHAR(len, bufp);
 	PUTCHAR(5, bufp); /* delay */
 	cbcp_send(us, CBCP_RESP, buf, len);
@@ -377,18 +485,181 @@
     }
 
     if (cb_type & ( 1 << CB_CONF_NO ) ) {
-        dbglog("cbcp_resp CONF_NO");
+	if (debug)
+            dbglog("cbcp_resp CONF_NO");
 	PUTCHAR(CB_CONF_NO, bufp);
-	len = 3;
+	len = 2;
 	PUTCHAR(len , bufp);
-	PUTCHAR(0, bufp);
 	cbcp_send(us, CBCP_RESP, buf, len);
-	start_networks();
 	return;
     }
 }
 
 static void
+cbcp_ack(us)
+    cbcp_state *us;
+{
+    u_char cb_type;
+    u_char buf[256];
+    u_char *bufp = buf;
+    int len = 0;
+
+    cb_type = us->us_allowed & us->us_type;
+    if(debug)
+        dbglog("cbcp_ack cb_type=%d", cb_type);
+
+    if (!cb_type) {
+        dbglog("Your remote side wanted a callback-type you don't allow -> doing no callback");
+        cb_type = 1 << CB_CONF_NO;
+        lcp_close(us->us_unit, "Invalid callback requested");
+        return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_USER ) ) {
+        if(debug)
+	     dbglog("cbcp_ack CONF_USER");
+	PUTCHAR(CB_CONF_USER, bufp);
+	len = 2 + 1 + strlen(us->us_number);
+	PUTCHAR(len , bufp);
+	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(1, bufp);
+	BCOPY(us->us_number, bufp, strlen(us->us_number));
+	cbcp_send(us, CBCP_ACK, buf, len);
+	cbcp_up(us);
+	return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+        if(debug)
+	     dbglog("cbcp_ack CONF_ADMIN");
+        PUTCHAR(CB_CONF_ADMIN, bufp);
+	len = 2 + 1;
+	PUTCHAR(len , bufp);
+	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(0, bufp);
+	cbcp_send(us, CBCP_ACK, buf, len);
+	cbcp_up(us);
+	return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_NO ) ) {
+        if(debug)
+             dbglog("cbcp_ack CONF_NO");
+	PUTCHAR(CB_CONF_NO, bufp);
+	len = 2;
+	PUTCHAR(len , bufp);
+	cbcp_send(us, CBCP_ACK, buf, len);
+	cbcp_up(us);
+	return;
+    }
+}
+
+static void
+cbcp_req(us)
+    cbcp_state *us;
+{
+    u_char cb_type;
+    u_char buf[256];
+    u_char *bufp = buf;
+    int len = 0;
+    
+    cb_type = us->us_type;
+
+    if (cb_type & ( 1 << CB_CONF_USER ) ) {
+        if(debug)
+	    dbglog("cbcp_req CONF_USER");
+	PUTCHAR(CB_CONF_USER, bufp);
+	len = 2 + 1 + strlen(us->us_number);
+	PUTCHAR(len , bufp);
+	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(1, bufp);
+	BCOPY(us->us_number, bufp, strlen(us->us_number));
+	cbcp_send(us, CBCP_REQ, buf, len);
+	return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+        if(debug)
+	    dbglog("cbcp_req CONF_ADMIN");
+        PUTCHAR(CB_CONF_ADMIN, bufp);
+	len = 2 + 1;
+	PUTCHAR(len , bufp);
+	PUTCHAR(5, bufp); /* delay */
+	PUTCHAR(0, bufp);
+	cbcp_send(us, CBCP_REQ, buf, len);
+	return;
+    }
+
+    if (cb_type & ( 1 << CB_CONF_NO ) ) {
+        if(debug)
+             dbglog("cbcp_req CONF_NO");
+	PUTCHAR(CB_CONF_NO, bufp);
+	len = 2;
+	PUTCHAR(len , bufp);
+	cbcp_send(us, CBCP_REQ, buf, len);
+	return;
+    }
+}
+
+/* received CBCP request */
+static void
+cbcp_recvresp(us, pckt, pcktlen)
+    cbcp_state *us;
+    char *pckt;
+    int pcktlen;
+{
+    u_char type, opt_len, delay, addr_type;
+    char address[256];
+    int len = pcktlen;
+
+    address[0] = 0;
+
+    if(debug)
+	dbglog("CBCP_RESP received");
+
+    while (len) {
+        if(debug)
+            dbglog("length: %d", len);
+
+	GETCHAR(type, pckt);
+	GETCHAR(opt_len, pckt);
+
+	if (opt_len > 2)
+	    GETCHAR(delay, pckt);
+
+	us->us_allowed |= (1 << type);
+
+	switch(type) {
+	case CB_CONF_NO:
+	    dbglog("Callback: none");
+	    break;
+
+	case CB_CONF_USER:
+	    if (opt_len > 4) {
+	        GETCHAR(addr_type, pckt);
+		memcpy(address, pckt, opt_len - 4);
+		address[opt_len - 4] = 0;
+	        dbglog("Callback: user callback, address: '%s'", address);
+	    }
+	    else
+	        dbglog("Callback: user callback");
+	    break;
+
+	case CB_CONF_ADMIN:
+	    dbglog("Callback: user admin defined");
+	    break;
+
+	case CB_CONF_LIST:
+	    break;
+	}
+	len -= opt_len;
+    }
+
+    cbcp_ack(us);
+}
+
+
+static void
 cbcp_send(us, code, buf, len)
     cbcp_state *us;
     u_char code;
@@ -436,10 +707,14 @@
 	    memcpy(address, pckt, opt_len - 4);
 	    address[opt_len - 4] = 0;
 	    if (address[0])
-	        dbglog("peer will call: %s", address);
+	        dbglog("Callback: peer will call: '%s'", address);
 	}
-	if (type == CB_CONF_NO)
+	if (type != CB_CONF_NO)
+	{
+	    persist = 0;
+	    lcp_close(0, "Call me back, please");
 	    return;
+	}
     }
 
     cbcp_up(us);
@@ -450,7 +725,6 @@
 cbcp_up(us)
     cbcp_state *us;
 {
-    persist = 0;
-    lcp_close(0, "Call me back, please");
+    network_phase(us->us_unit);
     status = EXIT_CALLBACK;
 }
