xref: /freebsd/usr.sbin/ppp/radius.c (revision 579abfd8954b5993725d227aa50167546e333009)
1972a1bcfSBrian Somers /*
2972a1bcfSBrian Somers  * Copyright 1999 Internet Business Solutions Ltd., Switzerland
3972a1bcfSBrian Somers  * All rights reserved.
4972a1bcfSBrian Somers  *
5972a1bcfSBrian Somers  * Redistribution and use in source and binary forms, with or without
6972a1bcfSBrian Somers  * modification, are permitted provided that the following conditions
7972a1bcfSBrian Somers  * are met:
8972a1bcfSBrian Somers  * 1. Redistributions of source code must retain the above copyright
9972a1bcfSBrian Somers  *    notice, this list of conditions and the following disclaimer.
10972a1bcfSBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
11972a1bcfSBrian Somers  *    notice, this list of conditions and the following disclaimer in the
12972a1bcfSBrian Somers  *    documentation and/or other materials provided with the distribution.
13972a1bcfSBrian Somers  *
14972a1bcfSBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15972a1bcfSBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16972a1bcfSBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17972a1bcfSBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18972a1bcfSBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19972a1bcfSBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20972a1bcfSBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21972a1bcfSBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22972a1bcfSBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23972a1bcfSBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24972a1bcfSBrian Somers  * SUCH DAMAGE.
25972a1bcfSBrian Somers  *
2697d92980SPeter Wemm  * $FreeBSD$
27972a1bcfSBrian Somers  *
28972a1bcfSBrian Somers  */
29972a1bcfSBrian Somers 
30972a1bcfSBrian Somers #include <sys/param.h>
318fb5ef5aSBrian Somers 
326b457978SBrian Somers #include <sys/socket.h>
33972a1bcfSBrian Somers #include <netinet/in_systm.h>
34972a1bcfSBrian Somers #include <netinet/in.h>
35972a1bcfSBrian Somers #include <netinet/ip.h>
36972a1bcfSBrian Somers #include <arpa/inet.h>
37972a1bcfSBrian Somers #include <sys/un.h>
386b457978SBrian Somers #include <net/route.h>
39972a1bcfSBrian Somers 
4010e629b9SBrian Somers #ifdef LOCALRAD
4110e629b9SBrian Somers #include "radlib.h"
42ff8e577bSBrian Somers #include "radlib_vs.h"
4310e629b9SBrian Somers #else
44972a1bcfSBrian Somers #include <radlib.h>
45ff8e577bSBrian Somers #include <radlib_vs.h>
4610e629b9SBrian Somers #endif
4710e629b9SBrian Somers 
4810e629b9SBrian Somers #include <errno.h>
498fb5ef5aSBrian Somers #ifndef NODES
508fb5ef5aSBrian Somers #include <md5.h>
518fb5ef5aSBrian Somers #endif
52972a1bcfSBrian Somers #include <stdio.h>
53972a1bcfSBrian Somers #include <stdlib.h>
54972a1bcfSBrian Somers #include <string.h>
55f0cdd9c0SBrian Somers #include <sys/time.h>
56972a1bcfSBrian Somers #include <termios.h>
57f10f5203SBrian Somers #include <unistd.h>
58f10f5203SBrian Somers #include <netdb.h>
59972a1bcfSBrian Somers 
605d9e6103SBrian Somers #include "layer.h"
61972a1bcfSBrian Somers #include "defs.h"
62972a1bcfSBrian Somers #include "log.h"
63972a1bcfSBrian Somers #include "descriptor.h"
64972a1bcfSBrian Somers #include "prompt.h"
65972a1bcfSBrian Somers #include "timer.h"
66972a1bcfSBrian Somers #include "fsm.h"
67972a1bcfSBrian Somers #include "iplist.h"
68972a1bcfSBrian Somers #include "slcompress.h"
69972a1bcfSBrian Somers #include "throughput.h"
70972a1bcfSBrian Somers #include "lqr.h"
71972a1bcfSBrian Somers #include "hdlc.h"
72972a1bcfSBrian Somers #include "mbuf.h"
7330949fd4SBrian Somers #include "ncpaddr.h"
7430949fd4SBrian Somers #include "ip.h"
75972a1bcfSBrian Somers #include "ipcp.h"
7630949fd4SBrian Somers #include "ipv6cp.h"
77972a1bcfSBrian Somers #include "route.h"
78972a1bcfSBrian Somers #include "command.h"
79972a1bcfSBrian Somers #include "filter.h"
80972a1bcfSBrian Somers #include "lcp.h"
81972a1bcfSBrian Somers #include "ccp.h"
82972a1bcfSBrian Somers #include "link.h"
83972a1bcfSBrian Somers #include "mp.h"
84972a1bcfSBrian Somers #include "radius.h"
85f0cdd9c0SBrian Somers #include "auth.h"
86f0cdd9c0SBrian Somers #include "async.h"
87f0cdd9c0SBrian Somers #include "physical.h"
88f0cdd9c0SBrian Somers #include "chat.h"
89f0cdd9c0SBrian Somers #include "cbcp.h"
90f0cdd9c0SBrian Somers #include "chap.h"
91f0cdd9c0SBrian Somers #include "datalink.h"
9230949fd4SBrian Somers #include "ncp.h"
93972a1bcfSBrian Somers #include "bundle.h"
94ff8e577bSBrian Somers #include "proto.h"
95ff8e577bSBrian Somers 
96ff8e577bSBrian Somers #ifndef NODES
97a16061b2SBrian Somers struct mschap_response {
98ff8e577bSBrian Somers   u_char ident;
99ff8e577bSBrian Somers   u_char flags;
100ff8e577bSBrian Somers   u_char lm_response[24];
101ff8e577bSBrian Somers   u_char nt_response[24];
102ff8e577bSBrian Somers };
103a16061b2SBrian Somers 
104a16061b2SBrian Somers struct mschap2_response {
105a16061b2SBrian Somers   u_char ident;
106a16061b2SBrian Somers   u_char flags;
107a16061b2SBrian Somers   u_char pchallenge[16];
108a16061b2SBrian Somers   u_char reserved[8];
109a16061b2SBrian Somers   u_char response[24];
110a16061b2SBrian Somers };
1118fb5ef5aSBrian Somers 
1128fb5ef5aSBrian Somers #define	AUTH_LEN	16
1138fb5ef5aSBrian Somers #define	SALT_LEN	2
1148fb5ef5aSBrian Somers #endif
1158fb5ef5aSBrian Somers 
1168fb5ef5aSBrian Somers static const char *
1178fb5ef5aSBrian Somers radius_policyname(int policy)
1188fb5ef5aSBrian Somers {
1198fb5ef5aSBrian Somers   switch(policy) {
1208fb5ef5aSBrian Somers   case MPPE_POLICY_ALLOWED:
1218fb5ef5aSBrian Somers     return "Allowed";
1228fb5ef5aSBrian Somers   case MPPE_POLICY_REQUIRED:
1238fb5ef5aSBrian Somers     return "Required";
1248fb5ef5aSBrian Somers   }
1258fb5ef5aSBrian Somers   return NumStr(policy, NULL, 0);
1268fb5ef5aSBrian Somers }
1278fb5ef5aSBrian Somers 
1288fb5ef5aSBrian Somers static const char *
1298fb5ef5aSBrian Somers radius_typesname(int types)
1308fb5ef5aSBrian Somers {
1318fb5ef5aSBrian Somers   switch(types) {
1328fb5ef5aSBrian Somers   case MPPE_TYPE_40BIT:
1338fb5ef5aSBrian Somers     return "40 bit";
1348fb5ef5aSBrian Somers   case MPPE_TYPE_128BIT:
1358fb5ef5aSBrian Somers     return "128 bit";
1368fb5ef5aSBrian Somers   case MPPE_TYPE_40BIT|MPPE_TYPE_128BIT:
1378fb5ef5aSBrian Somers     return "40 or 128 bit";
1388fb5ef5aSBrian Somers   }
1398fb5ef5aSBrian Somers   return NumStr(types, NULL, 0);
1408fb5ef5aSBrian Somers }
1418fb5ef5aSBrian Somers 
1428fb5ef5aSBrian Somers #ifndef NODES
1438fb5ef5aSBrian Somers static void
1448fb5ef5aSBrian Somers demangle(struct radius *r, const void *mangled, size_t mlen,
1458fb5ef5aSBrian Somers          char **buf, size_t *len)
1468fb5ef5aSBrian Somers {
1478fb5ef5aSBrian Somers   char R[AUTH_LEN];		/* variable names as per rfc2548 */
1488fb5ef5aSBrian Somers   const char *S;
1498fb5ef5aSBrian Somers   u_char b[16];
1508fb5ef5aSBrian Somers   const u_char *A, *C;
1518fb5ef5aSBrian Somers   MD5_CTX Context;
1528fb5ef5aSBrian Somers   int Slen, i, Clen, Ppos;
1538fb5ef5aSBrian Somers   u_char *P;
1548fb5ef5aSBrian Somers 
1558fb5ef5aSBrian Somers   if (mlen % 16 != SALT_LEN) {
1568fb5ef5aSBrian Somers     log_Printf(LogWARN, "Cannot interpret mangled data of length %ld\n",
1578fb5ef5aSBrian Somers                (u_long)mlen);
1588fb5ef5aSBrian Somers     *buf = NULL;
1598fb5ef5aSBrian Somers     *len = 0;
1608fb5ef5aSBrian Somers     return;
1618fb5ef5aSBrian Somers   }
1628fb5ef5aSBrian Somers 
1638fb5ef5aSBrian Somers   /* We need the RADIUS Request-Authenticator */
1648fb5ef5aSBrian Somers   if (rad_request_authenticator(r->cx.rad, R, sizeof R) != AUTH_LEN) {
1658fb5ef5aSBrian Somers     log_Printf(LogWARN, "Cannot obtain the RADIUS request authenticator\n");
1668fb5ef5aSBrian Somers     *buf = NULL;
1678fb5ef5aSBrian Somers     *len = 0;
1688fb5ef5aSBrian Somers     return;
1698fb5ef5aSBrian Somers   }
1708fb5ef5aSBrian Somers 
1718fb5ef5aSBrian Somers   A = (const u_char *)mangled;			/* Salt comes first */
1728fb5ef5aSBrian Somers   C = (const u_char *)mangled + SALT_LEN;	/* Then the ciphertext */
1738fb5ef5aSBrian Somers   Clen = mlen - SALT_LEN;
1748fb5ef5aSBrian Somers   S = rad_server_secret(r->cx.rad);		/* We need the RADIUS secret */
1758fb5ef5aSBrian Somers   Slen = strlen(S);
1768fb5ef5aSBrian Somers   P = alloca(Clen);				/* We derive our plaintext */
1778fb5ef5aSBrian Somers 
1788fb5ef5aSBrian Somers   MD5Init(&Context);
1798fb5ef5aSBrian Somers   MD5Update(&Context, S, Slen);
1808fb5ef5aSBrian Somers   MD5Update(&Context, R, AUTH_LEN);
1818fb5ef5aSBrian Somers   MD5Update(&Context, A, SALT_LEN);
1828fb5ef5aSBrian Somers   MD5Final(b, &Context);
1838fb5ef5aSBrian Somers   Ppos = 0;
1848fb5ef5aSBrian Somers 
1858fb5ef5aSBrian Somers   while (Clen) {
1868fb5ef5aSBrian Somers     Clen -= 16;
1878fb5ef5aSBrian Somers 
1888fb5ef5aSBrian Somers     for (i = 0; i < 16; i++)
1898fb5ef5aSBrian Somers       P[Ppos++] = C[i] ^ b[i];
1908fb5ef5aSBrian Somers 
1918fb5ef5aSBrian Somers     if (Clen) {
1928fb5ef5aSBrian Somers       MD5Init(&Context);
1938fb5ef5aSBrian Somers       MD5Update(&Context, S, Slen);
1948fb5ef5aSBrian Somers       MD5Update(&Context, C, 16);
1958fb5ef5aSBrian Somers       MD5Final(b, &Context);
1968fb5ef5aSBrian Somers     }
1978fb5ef5aSBrian Somers 
1988fb5ef5aSBrian Somers     C += 16;
1998fb5ef5aSBrian Somers   }
2008fb5ef5aSBrian Somers 
2018fb5ef5aSBrian Somers   /*
2028fb5ef5aSBrian Somers    * The resulting plain text consists of a one-byte length, the text and
2038fb5ef5aSBrian Somers    * maybe some padding.
2048fb5ef5aSBrian Somers    */
2058fb5ef5aSBrian Somers   *len = *P;
2068fb5ef5aSBrian Somers   if (*len > mlen - 1) {
2078fb5ef5aSBrian Somers     log_Printf(LogWARN, "Mangled data seems to be garbage\n");
2088fb5ef5aSBrian Somers     *buf = NULL;
2098fb5ef5aSBrian Somers     *len = 0;
2108fb5ef5aSBrian Somers     return;
2118fb5ef5aSBrian Somers   }
2128fb5ef5aSBrian Somers 
2138fb5ef5aSBrian Somers   *buf = malloc(*len);
2148fb5ef5aSBrian Somers   memcpy(*buf, P + 1, *len);
2158fb5ef5aSBrian Somers }
216ff8e577bSBrian Somers #endif
217972a1bcfSBrian Somers 
218f0cdd9c0SBrian Somers /*
219f0cdd9c0SBrian Somers  * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
220f0cdd9c0SBrian Somers  */
221f0cdd9c0SBrian Somers static void
222f0cdd9c0SBrian Somers radius_Process(struct radius *r, int got)
223972a1bcfSBrian Somers {
224972a1bcfSBrian Somers   char *argv[MAXARGS], *nuke;
225f0cdd9c0SBrian Somers   struct bundle *bundle;
226ff8e577bSBrian Somers   int argc, addrs, res, width;
22728e610e3SBrian Somers   size_t len;
22830949fd4SBrian Somers   struct ncprange dest;
22930949fd4SBrian Somers   struct ncpaddr gw;
230f0cdd9c0SBrian Somers   const void *data;
231c42627ffSBrian Somers   const char *stype;
232ff8e577bSBrian Somers   u_int32_t ipaddr, vendor;
23330949fd4SBrian Somers   struct in_addr ip;
234972a1bcfSBrian Somers 
235f0cdd9c0SBrian Somers   r->cx.fd = -1;		/* Stop select()ing */
236c42627ffSBrian Somers   stype = r->cx.auth ? "auth" : "acct";
237972a1bcfSBrian Somers 
238972a1bcfSBrian Somers   switch (got) {
239972a1bcfSBrian Somers     case RAD_ACCESS_ACCEPT:
240c42627ffSBrian Somers       log_Printf(LogPHASE, "Radius(%s): ACCEPT received\n", stype);
241c42627ffSBrian Somers       if (!r->cx.auth) {
242c42627ffSBrian Somers         rad_close(r->cx.rad);
243c42627ffSBrian Somers         return;
244c42627ffSBrian Somers       }
245972a1bcfSBrian Somers       break;
246972a1bcfSBrian Somers 
247f0cdd9c0SBrian Somers     case RAD_ACCESS_REJECT:
248c42627ffSBrian Somers       log_Printf(LogPHASE, "Radius(%s): REJECT received\n", stype);
249ff8e577bSBrian Somers       if (!r->cx.auth) {
250f0cdd9c0SBrian Somers         rad_close(r->cx.rad);
251f0cdd9c0SBrian Somers         return;
252ff8e577bSBrian Somers       }
253ff8e577bSBrian Somers       break;
254f0cdd9c0SBrian Somers 
255972a1bcfSBrian Somers     case RAD_ACCESS_CHALLENGE:
256972a1bcfSBrian Somers       /* we can't deal with this (for now) ! */
257f0cdd9c0SBrian Somers       log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
258c42627ffSBrian Somers       if (r->cx.auth)
259f0cdd9c0SBrian Somers         auth_Failure(r->cx.auth);
260f0cdd9c0SBrian Somers       rad_close(r->cx.rad);
261f0cdd9c0SBrian Somers       return;
262972a1bcfSBrian Somers 
263794c9bbcSBrian Somers     case RAD_ACCOUNTING_RESPONSE:
264c42627ffSBrian Somers       log_Printf(LogPHASE, "Radius(%s): Accounting response received\n", stype);
265c42627ffSBrian Somers       if (r->cx.auth)
266c42627ffSBrian Somers         auth_Failure(r->cx.auth);		/* unexpected !!! */
267c42627ffSBrian Somers 
268794c9bbcSBrian Somers       /* No further processing for accounting requests, please */
269794c9bbcSBrian Somers       rad_close(r->cx.rad);
270794c9bbcSBrian Somers       return;
271794c9bbcSBrian Somers 
272972a1bcfSBrian Somers     case -1:
273c42627ffSBrian Somers       log_Printf(LogPHASE, "radius(%s): %s\n", stype, rad_strerror(r->cx.rad));
274c42627ffSBrian Somers       if (r->cx.auth)
275f0cdd9c0SBrian Somers         auth_Failure(r->cx.auth);
276f0cdd9c0SBrian Somers       rad_close(r->cx.rad);
277f0cdd9c0SBrian Somers       return;
278972a1bcfSBrian Somers 
279972a1bcfSBrian Somers     default:
280c42627ffSBrian Somers       log_Printf(LogERROR, "rad_send_request(%s): Failed %d: %s\n", stype,
281f0cdd9c0SBrian Somers                  got, rad_strerror(r->cx.rad));
282c42627ffSBrian Somers       if (r->cx.auth)
283f0cdd9c0SBrian Somers         auth_Failure(r->cx.auth);
284f0cdd9c0SBrian Somers       rad_close(r->cx.rad);
285f0cdd9c0SBrian Somers       return;
286972a1bcfSBrian Somers   }
287972a1bcfSBrian Somers 
288ff8e577bSBrian Somers   /* Let's see what we've got in our reply */
289972a1bcfSBrian Somers   r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
290972a1bcfSBrian Somers   r->mtu = 0;
291972a1bcfSBrian Somers   r->vj = 0;
292ff8e577bSBrian Somers   while ((res = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
293ff8e577bSBrian Somers     switch (res) {
294972a1bcfSBrian Somers       case RAD_FRAMED_IP_ADDRESS:
295972a1bcfSBrian Somers         r->ip = rad_cvt_addr(data);
296f0cdd9c0SBrian Somers         log_Printf(LogPHASE, " IP %s\n", inet_ntoa(r->ip));
297972a1bcfSBrian Somers         break;
298972a1bcfSBrian Somers 
299bf1eaec5SBrian Somers       case RAD_FILTER_ID:
300bf1eaec5SBrian Somers         free(r->filterid);
301bf1eaec5SBrian Somers         if ((r->filterid = rad_cvt_string(data, len)) == NULL) {
302bf1eaec5SBrian Somers           log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
303ff8e577bSBrian Somers           auth_Failure(r->cx.auth);
304bf1eaec5SBrian Somers           rad_close(r->cx.rad);
305bf1eaec5SBrian Somers           return;
306bf1eaec5SBrian Somers         }
307bf1eaec5SBrian Somers         log_Printf(LogPHASE, " Filter \"%s\"\n", r->filterid);
308bf1eaec5SBrian Somers         break;
309bf1eaec5SBrian Somers 
310bf1eaec5SBrian Somers       case RAD_SESSION_TIMEOUT:
311bf1eaec5SBrian Somers         r->sessiontime = rad_cvt_int(data);
312bf1eaec5SBrian Somers         log_Printf(LogPHASE, " Session-Timeout %lu\n", r->sessiontime);
313bf1eaec5SBrian Somers         break;
314bf1eaec5SBrian Somers 
315972a1bcfSBrian Somers       case RAD_FRAMED_IP_NETMASK:
316972a1bcfSBrian Somers         r->mask = rad_cvt_addr(data);
317f0cdd9c0SBrian Somers         log_Printf(LogPHASE, " Netmask %s\n", inet_ntoa(r->mask));
318972a1bcfSBrian Somers         break;
319972a1bcfSBrian Somers 
320972a1bcfSBrian Somers       case RAD_FRAMED_MTU:
321972a1bcfSBrian Somers         r->mtu = rad_cvt_int(data);
322f0cdd9c0SBrian Somers         log_Printf(LogPHASE, " MTU %lu\n", r->mtu);
323972a1bcfSBrian Somers         break;
324972a1bcfSBrian Somers 
325972a1bcfSBrian Somers       case RAD_FRAMED_ROUTING:
326972a1bcfSBrian Somers         /* Disabled for now - should we automatically set up some filters ? */
327972a1bcfSBrian Somers         /* rad_cvt_int(data); */
328972a1bcfSBrian Somers         /* bit 1 = Send routing packets */
329972a1bcfSBrian Somers         /* bit 2 = Receive routing packets */
330972a1bcfSBrian Somers         break;
331972a1bcfSBrian Somers 
332972a1bcfSBrian Somers       case RAD_FRAMED_COMPRESSION:
333972a1bcfSBrian Somers         r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
334f0cdd9c0SBrian Somers         log_Printf(LogPHASE, " VJ %sabled\n", r->vj ? "en" : "dis");
335972a1bcfSBrian Somers         break;
336972a1bcfSBrian Somers 
337972a1bcfSBrian Somers       case RAD_FRAMED_ROUTE:
338972a1bcfSBrian Somers         /*
339972a1bcfSBrian Somers          * We expect a string of the format ``dest[/bits] gw [metrics]''
340972a1bcfSBrian Somers          * Any specified metrics are ignored.  MYADDR and HISADDR are
341972a1bcfSBrian Somers          * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
342972a1bcfSBrian Somers          * as ``HISADDR''.
343972a1bcfSBrian Somers          */
344972a1bcfSBrian Somers 
345972a1bcfSBrian Somers         if ((nuke = rad_cvt_string(data, len)) == NULL) {
346f0cdd9c0SBrian Somers           log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
347ff8e577bSBrian Somers           auth_Failure(r->cx.auth);
348f0cdd9c0SBrian Somers           rad_close(r->cx.rad);
349f0cdd9c0SBrian Somers           return;
350972a1bcfSBrian Somers         }
351972a1bcfSBrian Somers 
352f0cdd9c0SBrian Somers         log_Printf(LogPHASE, " Route: %s\n", nuke);
353f0cdd9c0SBrian Somers         bundle = r->cx.auth->physical->dl->bundle;
35430949fd4SBrian Somers         ip.s_addr = INADDR_ANY;
35530949fd4SBrian Somers         ncprange_setip4host(&dest, ip);
356972a1bcfSBrian Somers         argc = command_Interpret(nuke, strlen(nuke), argv);
357c39aa54eSBrian Somers         if (argc < 0)
358c39aa54eSBrian Somers           log_Printf(LogWARN, "radius: %s: Syntax error\n",
359c39aa54eSBrian Somers                      argc == 1 ? argv[0] : "\"\"");
360c39aa54eSBrian Somers         else if (argc < 2)
361972a1bcfSBrian Somers           log_Printf(LogWARN, "radius: %s: Invalid route\n",
362972a1bcfSBrian Somers                      argc == 1 ? argv[0] : "\"\"");
363972a1bcfSBrian Somers         else if ((strcasecmp(argv[0], "default") != 0 &&
36430949fd4SBrian Somers                   !ncprange_aton(&dest, &bundle->ncp, argv[0])) ||
36530949fd4SBrian Somers                  !ncpaddr_aton(&gw, &bundle->ncp, argv[1]))
366972a1bcfSBrian Somers           log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
367972a1bcfSBrian Somers                      argv[0], argv[1]);
368972a1bcfSBrian Somers         else {
36930949fd4SBrian Somers           ncprange_getwidth(&dest, &width);
37030949fd4SBrian Somers           if (width == 32 && strchr(argv[0], '/') == NULL) {
371972a1bcfSBrian Somers             /* No mask specified - use the natural mask */
37230949fd4SBrian Somers             ncprange_getip4addr(&dest, &ip);
37330949fd4SBrian Somers             ncprange_setip4mask(&dest, addr2mask(ip));
37430949fd4SBrian Somers           }
375972a1bcfSBrian Somers           addrs = 0;
376972a1bcfSBrian Somers 
377972a1bcfSBrian Somers           if (!strncasecmp(argv[0], "HISADDR", 7))
378972a1bcfSBrian Somers             addrs = ROUTE_DSTHISADDR;
379972a1bcfSBrian Somers           else if (!strncasecmp(argv[0], "MYADDR", 6))
380972a1bcfSBrian Somers             addrs = ROUTE_DSTMYADDR;
381972a1bcfSBrian Somers 
38230949fd4SBrian Somers           if (ncpaddr_getip4addr(&gw, &ipaddr) && ipaddr == INADDR_ANY) {
383972a1bcfSBrian Somers             addrs |= ROUTE_GWHISADDR;
38430949fd4SBrian Somers             ncpaddr_setip4(&gw, bundle->ncp.ipcp.peer_ip);
385972a1bcfSBrian Somers           } else if (strcasecmp(argv[1], "HISADDR") == 0)
386972a1bcfSBrian Somers             addrs |= ROUTE_GWHISADDR;
387972a1bcfSBrian Somers 
38830949fd4SBrian Somers           route_Add(&r->routes, addrs, &dest, &gw);
389972a1bcfSBrian Somers         }
390972a1bcfSBrian Somers         free(nuke);
391972a1bcfSBrian Somers         break;
392972a1bcfSBrian Somers 
393ff8e577bSBrian Somers       case RAD_REPLY_MESSAGE:
394ff8e577bSBrian Somers         free(r->repstr);
395ff8e577bSBrian Somers         if ((r->repstr = rad_cvt_string(data, len)) == NULL) {
396ff8e577bSBrian Somers           log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
397ff8e577bSBrian Somers           auth_Failure(r->cx.auth);
398ff8e577bSBrian Somers           rad_close(r->cx.rad);
399ff8e577bSBrian Somers           return;
400ff8e577bSBrian Somers         }
401ff8e577bSBrian Somers         log_Printf(LogPHASE, " Reply-Message \"%s\"\n", r->repstr);
402ff8e577bSBrian Somers         break;
403ff8e577bSBrian Somers 
404ff8e577bSBrian Somers       case RAD_VENDOR_SPECIFIC:
405ff8e577bSBrian Somers         if ((res = rad_get_vendor_attr(&vendor, &data, &len)) <= 0) {
406ff8e577bSBrian Somers           log_Printf(LogERROR, "rad_get_vendor_attr: %s (failing!)\n",
407f0cdd9c0SBrian Somers                      rad_strerror(r->cx.rad));
408f0cdd9c0SBrian Somers           auth_Failure(r->cx.auth);
409f0cdd9c0SBrian Somers           rad_close(r->cx.rad);
410ff8e577bSBrian Somers           return;
411ff8e577bSBrian Somers         }
412ff8e577bSBrian Somers 
413ff8e577bSBrian Somers 	switch (vendor) {
414ff8e577bSBrian Somers           case RAD_VENDOR_MICROSOFT:
415ff8e577bSBrian Somers             switch (res) {
4168fb5ef5aSBrian Somers #ifndef NODES
417ff8e577bSBrian Somers               case RAD_MICROSOFT_MS_CHAP_ERROR:
418ff8e577bSBrian Somers                 free(r->errstr);
419a95b23a6SBrian Somers                 if (len == 0)
420a95b23a6SBrian Somers                   r->errstr = NULL;
421a95b23a6SBrian Somers                 else {
42299cfc2e2SBrian Somers                   if (len < 3 || ((const char *)data)[1] != '=') {
42399cfc2e2SBrian Somers                     /*
42499cfc2e2SBrian Somers                      * Only point at the String field if we don't think the
42599cfc2e2SBrian Somers                      * peer has misformatted the response.
42699cfc2e2SBrian Somers                      */
42799cfc2e2SBrian Somers                     ((const char *)data)++;
42899cfc2e2SBrian Somers                     len--;
429579abfd8SBrian Somers                   } else
430579abfd8SBrian Somers                     log_Printf(LogWARN, "Warning: The MS-CHAP-Error "
431579abfd8SBrian Somers                                "attribute is mis-formatted.  Compensating\n");
43299cfc2e2SBrian Somers                   if ((r->errstr = rad_cvt_string((const char *)data,
43399cfc2e2SBrian Somers                                                   len)) == NULL) {
434ff8e577bSBrian Somers                     log_Printf(LogERROR, "rad_cvt_string: %s\n",
435ff8e577bSBrian Somers                                rad_strerror(r->cx.rad));
436ff8e577bSBrian Somers                     auth_Failure(r->cx.auth);
437ff8e577bSBrian Somers                     rad_close(r->cx.rad);
438ff8e577bSBrian Somers                     return;
439ff8e577bSBrian Somers                   }
440ff8e577bSBrian Somers                   log_Printf(LogPHASE, " MS-CHAP-Error \"%s\"\n", r->errstr);
441a95b23a6SBrian Somers                 }
442ff8e577bSBrian Somers                 break;
443ff8e577bSBrian Somers 
444a16061b2SBrian Somers               case RAD_MICROSOFT_MS_CHAP2_SUCCESS:
445a16061b2SBrian Somers                 free(r->msrepstr);
446a95b23a6SBrian Somers                 if (len == 0)
447a95b23a6SBrian Somers                   r->msrepstr = NULL;
448a95b23a6SBrian Somers                 else {
44999cfc2e2SBrian Somers                   if (len < 3 || ((const char *)data)[1] != '=') {
45099cfc2e2SBrian Somers                     /*
45199cfc2e2SBrian Somers                      * Only point at the String field if we don't think the
45299cfc2e2SBrian Somers                      * peer has misformatted the response.
45399cfc2e2SBrian Somers                      */
45499cfc2e2SBrian Somers                     ((const char *)data)++;
45599cfc2e2SBrian Somers                     len--;
456579abfd8SBrian Somers                   } else
457579abfd8SBrian Somers                     log_Printf(LogWARN, "Warning: The MS-CHAP2-Success "
458579abfd8SBrian Somers                                "attribute is mis-formatted.  Compensating\n");
45999cfc2e2SBrian Somers                   if ((r->msrepstr = rad_cvt_string((const char *)data,
46099cfc2e2SBrian Somers                                                     len)) == NULL) {
461a16061b2SBrian Somers                     log_Printf(LogERROR, "rad_cvt_string: %s\n",
462a16061b2SBrian Somers                                rad_strerror(r->cx.rad));
463a16061b2SBrian Somers                     auth_Failure(r->cx.auth);
464a16061b2SBrian Somers                     rad_close(r->cx.rad);
465a16061b2SBrian Somers                     return;
466a16061b2SBrian Somers                   }
467a95b23a6SBrian Somers                   log_Printf(LogPHASE, " MS-CHAP2-Success \"%s\"\n",
468a95b23a6SBrian Somers                              r->msrepstr);
469a95b23a6SBrian Somers                 }
470a16061b2SBrian Somers                 break;
471a16061b2SBrian Somers 
4728fb5ef5aSBrian Somers               case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
4738fb5ef5aSBrian Somers                 r->mppe.policy = rad_cvt_int(data);
4748fb5ef5aSBrian Somers                 log_Printf(LogPHASE, " MS-MPPE-Encryption-Policy %s\n",
4758fb5ef5aSBrian Somers                            radius_policyname(r->mppe.policy));
4768fb5ef5aSBrian Somers                 break;
4778fb5ef5aSBrian Somers 
4788fb5ef5aSBrian Somers               case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
4798fb5ef5aSBrian Somers                 r->mppe.types = rad_cvt_int(data);
4808fb5ef5aSBrian Somers                 log_Printf(LogPHASE, " MS-MPPE-Encryption-Types %s\n",
4818fb5ef5aSBrian Somers                            radius_typesname(r->mppe.types));
4828fb5ef5aSBrian Somers                 break;
4838fb5ef5aSBrian Somers 
4848fb5ef5aSBrian Somers               case RAD_MICROSOFT_MS_MPPE_RECV_KEY:
4858fb5ef5aSBrian Somers                 free(r->mppe.recvkey);
4868fb5ef5aSBrian Somers 		demangle(r, data, len, &r->mppe.recvkey, &r->mppe.recvkeylen);
4878fb5ef5aSBrian Somers                 log_Printf(LogPHASE, " MS-MPPE-Recv-Key ********\n");
4888fb5ef5aSBrian Somers                 break;
4898fb5ef5aSBrian Somers 
4908fb5ef5aSBrian Somers               case RAD_MICROSOFT_MS_MPPE_SEND_KEY:
4918fb5ef5aSBrian Somers 		demangle(r, data, len, &r->mppe.sendkey, &r->mppe.sendkeylen);
4928fb5ef5aSBrian Somers                 log_Printf(LogPHASE, " MS-MPPE-Send-Key ********\n");
4938fb5ef5aSBrian Somers                 break;
4948fb5ef5aSBrian Somers #endif
4958fb5ef5aSBrian Somers 
496ff8e577bSBrian Somers               default:
497ff8e577bSBrian Somers                 log_Printf(LogDEBUG, "Dropping MICROSOFT vendor specific "
498ff8e577bSBrian Somers                            "RADIUS attribute %d\n", res);
499ff8e577bSBrian Somers                 break;
500ff8e577bSBrian Somers             }
501ff8e577bSBrian Somers             break;
502ff8e577bSBrian Somers 
503ff8e577bSBrian Somers           default:
504ff8e577bSBrian Somers             log_Printf(LogDEBUG, "Dropping vendor %lu RADIUS attribute %d\n",
505ff8e577bSBrian Somers                        (unsigned long)vendor, res);
506ff8e577bSBrian Somers             break;
507ff8e577bSBrian Somers         }
508ff8e577bSBrian Somers         break;
509ff8e577bSBrian Somers 
510ff8e577bSBrian Somers       default:
511ff8e577bSBrian Somers         log_Printf(LogDEBUG, "Dropping RADIUS attribute %d\n", res);
512ff8e577bSBrian Somers         break;
513ff8e577bSBrian Somers     }
514ff8e577bSBrian Somers   }
515ff8e577bSBrian Somers 
516ff8e577bSBrian Somers   if (res == -1) {
517ff8e577bSBrian Somers     log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
518ff8e577bSBrian Somers                rad_strerror(r->cx.rad));
519ff8e577bSBrian Somers     auth_Failure(r->cx.auth);
520ff8e577bSBrian Somers   } else if (got == RAD_ACCESS_REJECT)
521ff8e577bSBrian Somers     auth_Failure(r->cx.auth);
522ff8e577bSBrian Somers   else {
523f0cdd9c0SBrian Somers     r->valid = 1;
524f0cdd9c0SBrian Somers     auth_Success(r->cx.auth);
525f0cdd9c0SBrian Somers   }
526ff8e577bSBrian Somers   rad_close(r->cx.rad);
527972a1bcfSBrian Somers }
528972a1bcfSBrian Somers 
529f0cdd9c0SBrian Somers /*
5308e7bd08eSBrian Somers  * We've either timed out or select()ed on the read descriptor
531f0cdd9c0SBrian Somers  */
532f0cdd9c0SBrian Somers static void
533f0cdd9c0SBrian Somers radius_Continue(struct radius *r, int sel)
534f0cdd9c0SBrian Somers {
535f0cdd9c0SBrian Somers   struct timeval tv;
536f0cdd9c0SBrian Somers   int got;
537972a1bcfSBrian Somers 
538f0cdd9c0SBrian Somers   timer_Stop(&r->cx.timer);
539f0cdd9c0SBrian Somers   if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
540f0cdd9c0SBrian Somers     log_Printf(LogPHASE, "Radius: Request re-sent\n");
541f0cdd9c0SBrian Somers     r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
542f0cdd9c0SBrian Somers     timer_Start(&r->cx.timer);
543f0cdd9c0SBrian Somers     return;
544f0cdd9c0SBrian Somers   }
545f0cdd9c0SBrian Somers 
546f0cdd9c0SBrian Somers   radius_Process(r, got);
547f0cdd9c0SBrian Somers }
548f0cdd9c0SBrian Somers 
549f0cdd9c0SBrian Somers /*
550f0cdd9c0SBrian Somers  * Time to call rad_continue_send_request() - timed out.
551f0cdd9c0SBrian Somers  */
552f0cdd9c0SBrian Somers static void
553f0cdd9c0SBrian Somers radius_Timeout(void *v)
554f0cdd9c0SBrian Somers {
555f0cdd9c0SBrian Somers   radius_Continue((struct radius *)v, 0);
556f0cdd9c0SBrian Somers }
557f0cdd9c0SBrian Somers 
558f0cdd9c0SBrian Somers /*
559f0cdd9c0SBrian Somers  * Time to call rad_continue_send_request() - something to read.
560f0cdd9c0SBrian Somers  */
561f0cdd9c0SBrian Somers static void
562f013f33eSBrian Somers radius_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
563f0cdd9c0SBrian Somers {
564f0cdd9c0SBrian Somers   radius_Continue(descriptor2radius(d), 1);
565f0cdd9c0SBrian Somers }
566f0cdd9c0SBrian Somers 
567f0cdd9c0SBrian Somers /*
5688e7bd08eSBrian Somers  * Behave as a struct fdescriptor (descriptor.h)
569f0cdd9c0SBrian Somers  */
570f0cdd9c0SBrian Somers static int
571f013f33eSBrian Somers radius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
572f0cdd9c0SBrian Somers {
573f0cdd9c0SBrian Somers   struct radius *rad = descriptor2radius(d);
574f0cdd9c0SBrian Somers 
575f0cdd9c0SBrian Somers   if (r && rad->cx.fd != -1) {
576f0cdd9c0SBrian Somers     FD_SET(rad->cx.fd, r);
577f0cdd9c0SBrian Somers     if (*n < rad->cx.fd + 1)
578f0cdd9c0SBrian Somers       *n = rad->cx.fd + 1;
579f0cdd9c0SBrian Somers     log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
58082d6780cSBrian Somers     return 1;
581972a1bcfSBrian Somers   }
582972a1bcfSBrian Somers 
583f0cdd9c0SBrian Somers   return 0;
584f0cdd9c0SBrian Somers }
585f0cdd9c0SBrian Somers 
586f0cdd9c0SBrian Somers /*
5878e7bd08eSBrian Somers  * Behave as a struct fdescriptor (descriptor.h)
588f0cdd9c0SBrian Somers  */
589f0cdd9c0SBrian Somers static int
590f013f33eSBrian Somers radius_IsSet(struct fdescriptor *d, const fd_set *fdset)
591f0cdd9c0SBrian Somers {
592f0cdd9c0SBrian Somers   struct radius *r = descriptor2radius(d);
593f0cdd9c0SBrian Somers 
594f0cdd9c0SBrian Somers   return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
595f0cdd9c0SBrian Somers }
596f0cdd9c0SBrian Somers 
597f0cdd9c0SBrian Somers /*
5988e7bd08eSBrian Somers  * Behave as a struct fdescriptor (descriptor.h)
599f0cdd9c0SBrian Somers  */
600f0cdd9c0SBrian Somers static int
601f013f33eSBrian Somers radius_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
602f0cdd9c0SBrian Somers {
603f0cdd9c0SBrian Somers   /* We never want to write here ! */
604f0cdd9c0SBrian Somers   log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
605f0cdd9c0SBrian Somers   return 0;
606f0cdd9c0SBrian Somers }
607f0cdd9c0SBrian Somers 
608f0cdd9c0SBrian Somers /*
609f0cdd9c0SBrian Somers  * Initialise ourselves
610f0cdd9c0SBrian Somers  */
611f0cdd9c0SBrian Somers void
612f0cdd9c0SBrian Somers radius_Init(struct radius *r)
613f0cdd9c0SBrian Somers {
614f0cdd9c0SBrian Somers   r->desc.type = RADIUS_DESCRIPTOR;
615f0cdd9c0SBrian Somers   r->desc.UpdateSet = radius_UpdateSet;
616f0cdd9c0SBrian Somers   r->desc.IsSet = radius_IsSet;
617f0cdd9c0SBrian Somers   r->desc.Read = radius_Read;
618f0cdd9c0SBrian Somers   r->desc.Write = radius_Write;
619ff8e577bSBrian Somers   r->cx.fd = -1;
620ff8e577bSBrian Somers   r->cx.rad = NULL;
621f0cdd9c0SBrian Somers   memset(&r->cx.timer, '\0', sizeof r->cx.timer);
622ff8e577bSBrian Somers   r->cx.auth = NULL;
623ff8e577bSBrian Somers   r->valid = 0;
624ff8e577bSBrian Somers   r->vj = 0;
625ff8e577bSBrian Somers   r->ip.s_addr = INADDR_ANY;
626ff8e577bSBrian Somers   r->mask.s_addr = INADDR_NONE;
627ff8e577bSBrian Somers   r->routes = NULL;
628ff8e577bSBrian Somers   r->mtu = DEF_MTU;
629a16061b2SBrian Somers   r->msrepstr = NULL;
630ff8e577bSBrian Somers   r->repstr = NULL;
631ff8e577bSBrian Somers   r->errstr = NULL;
6328fb5ef5aSBrian Somers   r->mppe.policy = 0;
6338fb5ef5aSBrian Somers   r->mppe.types = 0;
6348fb5ef5aSBrian Somers   r->mppe.recvkey = NULL;
6358fb5ef5aSBrian Somers   r->mppe.recvkeylen = 0;
6368fb5ef5aSBrian Somers   r->mppe.sendkey = NULL;
6378fb5ef5aSBrian Somers   r->mppe.sendkeylen = 0;
638ff8e577bSBrian Somers   *r->cfg.file = '\0';;
639794c9bbcSBrian Somers   log_Printf(LogDEBUG, "Radius: radius_Init\n");
640f0cdd9c0SBrian Somers }
641f0cdd9c0SBrian Somers 
642f0cdd9c0SBrian Somers /*
643f0cdd9c0SBrian Somers  * Forget everything and go back to initialised state.
644f0cdd9c0SBrian Somers  */
645f0cdd9c0SBrian Somers void
646f0cdd9c0SBrian Somers radius_Destroy(struct radius *r)
647f0cdd9c0SBrian Somers {
648f0cdd9c0SBrian Somers   r->valid = 0;
649794c9bbcSBrian Somers   log_Printf(LogDEBUG, "Radius: radius_Destroy\n");
650f0cdd9c0SBrian Somers   timer_Stop(&r->cx.timer);
651f0cdd9c0SBrian Somers   route_DeleteAll(&r->routes);
652bf1eaec5SBrian Somers   free(r->filterid);
653bf1eaec5SBrian Somers   r->filterid = NULL;
654a16061b2SBrian Somers   free(r->msrepstr);
655a16061b2SBrian Somers   r->msrepstr = NULL;
656ff8e577bSBrian Somers   free(r->repstr);
657ff8e577bSBrian Somers   r->repstr = NULL;
658ff8e577bSBrian Somers   free(r->errstr);
659ff8e577bSBrian Somers   r->errstr = NULL;
6608fb5ef5aSBrian Somers   free(r->mppe.recvkey);
6618fb5ef5aSBrian Somers   r->mppe.recvkey = NULL;
6628fb5ef5aSBrian Somers   r->mppe.recvkeylen = 0;
6638fb5ef5aSBrian Somers   free(r->mppe.sendkey);
6648fb5ef5aSBrian Somers   r->mppe.sendkey = NULL;
6658fb5ef5aSBrian Somers   r->mppe.sendkeylen = 0;
666f0cdd9c0SBrian Somers   if (r->cx.fd != -1) {
667f0cdd9c0SBrian Somers     r->cx.fd = -1;
668f0cdd9c0SBrian Somers     rad_close(r->cx.rad);
669f0cdd9c0SBrian Somers   }
670f0cdd9c0SBrian Somers }
671f0cdd9c0SBrian Somers 
672de59e178SBrian Somers static int
673de59e178SBrian Somers radius_put_physical_details(struct rad_handle *rad, struct physical *p)
674de59e178SBrian Somers {
675de59e178SBrian Somers   int slot, type;
676de59e178SBrian Somers 
677de59e178SBrian Somers   type = RAD_VIRTUAL;
678de59e178SBrian Somers   if (p->handler)
679de59e178SBrian Somers     switch (p->handler->type) {
680de59e178SBrian Somers       case I4B_DEVICE:
681de59e178SBrian Somers         type = RAD_ISDN_SYNC;
682de59e178SBrian Somers         break;
683de59e178SBrian Somers 
684de59e178SBrian Somers       case TTY_DEVICE:
685de59e178SBrian Somers         type = RAD_ASYNC;
686de59e178SBrian Somers         break;
687de59e178SBrian Somers 
688de59e178SBrian Somers       case ETHER_DEVICE:
689de59e178SBrian Somers         type = RAD_ETHERNET;
690de59e178SBrian Somers         break;
691de59e178SBrian Somers 
692de59e178SBrian Somers       case TCP_DEVICE:
693de59e178SBrian Somers       case UDP_DEVICE:
694de59e178SBrian Somers       case EXEC_DEVICE:
695de59e178SBrian Somers       case ATM_DEVICE:
696de59e178SBrian Somers       case NG_DEVICE:
697de59e178SBrian Somers         type = RAD_VIRTUAL;
698de59e178SBrian Somers         break;
699de59e178SBrian Somers     }
700de59e178SBrian Somers 
701de59e178SBrian Somers   if (rad_put_int(rad, RAD_NAS_PORT_TYPE, type) != 0) {
702de59e178SBrian Somers     log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
703de59e178SBrian Somers     rad_close(rad);
704de59e178SBrian Somers     return 0;
705de59e178SBrian Somers   }
706de59e178SBrian Somers 
707de59e178SBrian Somers   if ((slot = physical_Slot(p)) >= 0)
708de59e178SBrian Somers     if (rad_put_int(rad, RAD_NAS_PORT, slot) != 0) {
709de59e178SBrian Somers       log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
710de59e178SBrian Somers       rad_close(rad);
711de59e178SBrian Somers       return 0;
712de59e178SBrian Somers     }
713de59e178SBrian Somers 
714de59e178SBrian Somers   return 1;
715de59e178SBrian Somers }
716de59e178SBrian Somers 
717f0cdd9c0SBrian Somers /*
718f0cdd9c0SBrian Somers  * Start an authentication request to the RADIUS server.
719f0cdd9c0SBrian Somers  */
720a16061b2SBrian Somers int
721f0cdd9c0SBrian Somers radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
722a16061b2SBrian Somers                     const char *key, int klen, const char *nchallenge,
723250be50bSBrian Somers                     int nclen)
724f0cdd9c0SBrian Somers {
725f0cdd9c0SBrian Somers   struct timeval tv;
726de59e178SBrian Somers   int got;
72726e6a622SBrian Somers   char hostname[MAXHOSTNAMELEN];
728aadbb4eaSBrian Somers #if 0
729f10f5203SBrian Somers   struct hostent *hp;
730f10f5203SBrian Somers   struct in_addr hostaddr;
7318fb5ef5aSBrian Somers #endif
732ff8e577bSBrian Somers #ifndef NODES
733a16061b2SBrian Somers   struct mschap_response msresp;
734a16061b2SBrian Somers   struct mschap2_response msresp2;
735250be50bSBrian Somers   const struct MSCHAPv2_resp *keyv2;
736ff8e577bSBrian Somers #endif
737f0cdd9c0SBrian Somers 
738f0cdd9c0SBrian Somers   if (!*r->cfg.file)
739a16061b2SBrian Somers     return 0;
740f0cdd9c0SBrian Somers 
741f0cdd9c0SBrian Somers   if (r->cx.fd != -1)
742f0cdd9c0SBrian Somers     /*
743f0cdd9c0SBrian Somers      * We assume that our name/key/challenge is the same as last time,
744f0cdd9c0SBrian Somers      * and just continue to wait for the RADIUS server(s).
745f0cdd9c0SBrian Somers      */
746a16061b2SBrian Somers     return 1;
747f0cdd9c0SBrian Somers 
748f0cdd9c0SBrian Somers   radius_Destroy(r);
749f0cdd9c0SBrian Somers 
750794c9bbcSBrian Somers   if ((r->cx.rad = rad_auth_open()) == NULL) {
751794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
752a16061b2SBrian Somers     return 0;
753f0cdd9c0SBrian Somers   }
754f0cdd9c0SBrian Somers 
755f0cdd9c0SBrian Somers   if (rad_config(r->cx.rad, r->cfg.file) != 0) {
756f0cdd9c0SBrian Somers     log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
757f0cdd9c0SBrian Somers     rad_close(r->cx.rad);
758a16061b2SBrian Somers     return 0;
759f0cdd9c0SBrian Somers   }
760f0cdd9c0SBrian Somers 
761f0cdd9c0SBrian Somers   if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
762f0cdd9c0SBrian Somers     log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
763f0cdd9c0SBrian Somers     rad_close(r->cx.rad);
764a16061b2SBrian Somers     return 0;
765f0cdd9c0SBrian Somers   }
766f0cdd9c0SBrian Somers 
767f0cdd9c0SBrian Somers   if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
768f0cdd9c0SBrian Somers       rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
769f0cdd9c0SBrian Somers       rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
770f0cdd9c0SBrian Somers     log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
771f0cdd9c0SBrian Somers     rad_close(r->cx.rad);
772a16061b2SBrian Somers     return 0;
773f0cdd9c0SBrian Somers   }
774f0cdd9c0SBrian Somers 
775ff8e577bSBrian Somers   switch (authp->physical->link.lcp.want_auth) {
776ff8e577bSBrian Somers   case PROTO_PAP:
777ff8e577bSBrian Somers     /* We're talking PAP */
778ff8e577bSBrian Somers     if (rad_put_attr(r->cx.rad, RAD_USER_PASSWORD, key, klen) != 0) {
779ff8e577bSBrian Somers       log_Printf(LogERROR, "PAP: rad_put_string: %s\n",
780ff8e577bSBrian Somers                  rad_strerror(r->cx.rad));
781ff8e577bSBrian Somers       rad_close(r->cx.rad);
782a16061b2SBrian Somers       return 0;
783ff8e577bSBrian Somers     }
784ff8e577bSBrian Somers     break;
785ff8e577bSBrian Somers 
786ff8e577bSBrian Somers   case PROTO_CHAP:
787ff8e577bSBrian Somers     switch (authp->physical->link.lcp.want_authtype) {
788ff8e577bSBrian Somers     case 0x5:
78950ca6ec3SBrian Somers       if (rad_put_attr(r->cx.rad, RAD_CHAP_PASSWORD, key, klen) != 0 ||
790a16061b2SBrian Somers           rad_put_attr(r->cx.rad, RAD_CHAP_CHALLENGE, nchallenge, nclen) != 0) {
791f0cdd9c0SBrian Somers         log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
792f0cdd9c0SBrian Somers                    rad_strerror(r->cx.rad));
793f0cdd9c0SBrian Somers         rad_close(r->cx.rad);
794a16061b2SBrian Somers         return 0;
795f0cdd9c0SBrian Somers       }
796ff8e577bSBrian Somers       break;
797ff8e577bSBrian Somers 
798ff8e577bSBrian Somers #ifndef NODES
799ff8e577bSBrian Somers     case 0x80:
800ff8e577bSBrian Somers       if (klen != 50) {
801a16061b2SBrian Somers         log_Printf(LogERROR, "CHAP80: Unrecognised key length %d\n", klen);
802f0cdd9c0SBrian Somers         rad_close(r->cx.rad);
803a16061b2SBrian Somers         return 0;
804f0cdd9c0SBrian Somers       }
805a16061b2SBrian Somers 
806ff8e577bSBrian Somers       rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
807a16061b2SBrian Somers                           RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
808a16061b2SBrian Somers       msresp.ident = *key;
809a16061b2SBrian Somers       msresp.flags = 0x01;
810a16061b2SBrian Somers       memcpy(msresp.lm_response, key + 1, 24);
811a16061b2SBrian Somers       memcpy(msresp.nt_response, key + 25, 24);
812ff8e577bSBrian Somers       rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
813a16061b2SBrian Somers                           RAD_MICROSOFT_MS_CHAP_RESPONSE, &msresp,
814a16061b2SBrian Somers                           sizeof msresp);
815ff8e577bSBrian Somers       break;
816ff8e577bSBrian Somers 
817ff8e577bSBrian Somers     case 0x81:
818250be50bSBrian Somers       if (klen != sizeof(*keyv2) + 1) {
819a16061b2SBrian Somers         log_Printf(LogERROR, "CHAP81: Unrecognised key length %d\n", klen);
820a16061b2SBrian Somers         rad_close(r->cx.rad);
821a16061b2SBrian Somers         return 0;
822a16061b2SBrian Somers       }
823a16061b2SBrian Somers 
824250be50bSBrian Somers       keyv2 = (const struct MSCHAPv2_resp *)(key + 1);
825a16061b2SBrian Somers       rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
826a16061b2SBrian Somers                           RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
827a16061b2SBrian Somers       msresp2.ident = *key;
828250be50bSBrian Somers       msresp2.flags = keyv2->Flags;
829250be50bSBrian Somers       memcpy(msresp2.response, keyv2->NTResponse, sizeof msresp2.response);
830a16061b2SBrian Somers       memset(msresp2.reserved, '\0', sizeof msresp2.reserved);
831250be50bSBrian Somers       memcpy(msresp2.pchallenge, keyv2->PeerChallenge,
832250be50bSBrian Somers              sizeof msresp2.pchallenge);
833a16061b2SBrian Somers       rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
834a16061b2SBrian Somers                           RAD_MICROSOFT_MS_CHAP2_RESPONSE, &msresp2,
835a16061b2SBrian Somers                           sizeof msresp2);
836a16061b2SBrian Somers       break;
837ff8e577bSBrian Somers #endif
838ff8e577bSBrian Somers     default:
839ff8e577bSBrian Somers       log_Printf(LogERROR, "CHAP: Unrecognised type 0x%02x\n",
840ff8e577bSBrian Somers                  authp->physical->link.lcp.want_authtype);
841ff8e577bSBrian Somers       rad_close(r->cx.rad);
842a16061b2SBrian Somers       return 0;
843ff8e577bSBrian Somers     }
844ff8e577bSBrian Somers   }
845f0cdd9c0SBrian Somers 
846f10f5203SBrian Somers   if (gethostname(hostname, sizeof hostname) != 0)
847f10f5203SBrian Somers     log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
848f10f5203SBrian Somers   else {
849aadbb4eaSBrian Somers #if 0
850f10f5203SBrian Somers     if ((hp = gethostbyname(hostname)) != NULL) {
851f10f5203SBrian Somers       hostaddr.s_addr = *(u_long *)hp->h_addr;
852f10f5203SBrian Somers       if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
853f10f5203SBrian Somers         log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
854f10f5203SBrian Somers                    rad_strerror(r->cx.rad));
855f10f5203SBrian Somers         rad_close(r->cx.rad);
856a16061b2SBrian Somers         return 0;
857f10f5203SBrian Somers       }
858f10f5203SBrian Somers     }
859aadbb4eaSBrian Somers #endif
860f10f5203SBrian Somers     if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
861f10f5203SBrian Somers       log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
862f10f5203SBrian Somers                  rad_strerror(r->cx.rad));
863f10f5203SBrian Somers       rad_close(r->cx.rad);
864a16061b2SBrian Somers       return 0;
865f10f5203SBrian Somers     }
866f10f5203SBrian Somers   }
867f10f5203SBrian Somers 
868de59e178SBrian Somers   radius_put_physical_details(r->cx.rad, authp->physical);
869f10f5203SBrian Somers 
870c42627ffSBrian Somers   r->cx.auth = authp;
871f0cdd9c0SBrian Somers   if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
872f0cdd9c0SBrian Somers     radius_Process(r, got);
873f0cdd9c0SBrian Somers   else {
874f0cdd9c0SBrian Somers     log_Printf(LogPHASE, "Radius: Request sent\n");
875f0cdd9c0SBrian Somers     log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
876f0cdd9c0SBrian Somers     r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
877f0cdd9c0SBrian Somers     r->cx.timer.func = radius_Timeout;
878c42627ffSBrian Somers     r->cx.timer.name = "radius auth";
879f0cdd9c0SBrian Somers     r->cx.timer.arg = r;
880f0cdd9c0SBrian Somers     timer_Start(&r->cx.timer);
881f0cdd9c0SBrian Somers   }
882a16061b2SBrian Somers 
883a16061b2SBrian Somers   return 1;
884f0cdd9c0SBrian Somers }
885f0cdd9c0SBrian Somers 
886f0cdd9c0SBrian Somers /*
887794c9bbcSBrian Somers  * Send an accounting request to the RADIUS server
888794c9bbcSBrian Somers  */
889794c9bbcSBrian Somers void
890794c9bbcSBrian Somers radius_Account(struct radius *r, struct radacct *ac, struct datalink *dl,
891794c9bbcSBrian Somers                int acct_type, struct in_addr *peer_ip, struct in_addr *netmask,
892794c9bbcSBrian Somers                struct pppThroughput *stats)
893794c9bbcSBrian Somers {
894794c9bbcSBrian Somers   struct timeval tv;
895de59e178SBrian Somers   int got;
89626e6a622SBrian Somers   char hostname[MAXHOSTNAMELEN];
897aadbb4eaSBrian Somers #if 0
898794c9bbcSBrian Somers   struct hostent *hp;
899794c9bbcSBrian Somers   struct in_addr hostaddr;
9008fb5ef5aSBrian Somers #endif
901794c9bbcSBrian Somers 
902794c9bbcSBrian Somers   if (!*r->cfg.file)
903794c9bbcSBrian Somers     return;
904794c9bbcSBrian Somers 
905794c9bbcSBrian Somers   if (r->cx.fd != -1)
906794c9bbcSBrian Somers     /*
907794c9bbcSBrian Somers      * We assume that our name/key/challenge is the same as last time,
908794c9bbcSBrian Somers      * and just continue to wait for the RADIUS server(s).
909794c9bbcSBrian Somers      */
910794c9bbcSBrian Somers     return;
911794c9bbcSBrian Somers 
9128fb5ef5aSBrian Somers   timer_Stop(&r->cx.timer);
913794c9bbcSBrian Somers 
914ba093e81SBrian Somers   if ((r->cx.rad = rad_acct_open()) == NULL) {
915794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
916794c9bbcSBrian Somers     return;
917794c9bbcSBrian Somers   }
918794c9bbcSBrian Somers 
919794c9bbcSBrian Somers   if (rad_config(r->cx.rad, r->cfg.file) != 0) {
920794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
921794c9bbcSBrian Somers     rad_close(r->cx.rad);
922794c9bbcSBrian Somers     return;
923794c9bbcSBrian Somers   }
924794c9bbcSBrian Somers 
925794c9bbcSBrian Somers   if (rad_create_request(r->cx.rad, RAD_ACCOUNTING_REQUEST) != 0) {
926794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
927794c9bbcSBrian Somers     rad_close(r->cx.rad);
928794c9bbcSBrian Somers     return;
929794c9bbcSBrian Somers   }
930794c9bbcSBrian Somers 
931794c9bbcSBrian Somers   /* Grab some accounting data and initialize structure */
932794c9bbcSBrian Somers   if (acct_type == RAD_START) {
933794c9bbcSBrian Somers     ac->rad_parent = r;
934794c9bbcSBrian Somers     /* Fetch username from datalink */
935794c9bbcSBrian Somers     strncpy(ac->user_name, dl->peer.authname, sizeof ac->user_name);
936794c9bbcSBrian Somers     ac->user_name[AUTHLEN-1] = '\0';
937794c9bbcSBrian Somers 
938794c9bbcSBrian Somers     ac->authentic = 2;		/* Assume RADIUS verified auth data */
939794c9bbcSBrian Somers 
940794c9bbcSBrian Somers     /* Generate a session ID */
94112b5aabaSBrian Somers     snprintf(ac->session_id, sizeof ac->session_id, "%s%ld-%s%lu",
94212b5aabaSBrian Somers              dl->bundle->cfg.auth.name, (long)getpid(),
943794c9bbcSBrian Somers              dl->peer.authname, (unsigned long)stats->uptime);
944794c9bbcSBrian Somers 
945794c9bbcSBrian Somers     /* And grab our MP socket name */
946794c9bbcSBrian Somers     snprintf(ac->multi_session_id, sizeof ac->multi_session_id, "%s",
947794c9bbcSBrian Somers              dl->bundle->ncp.mp.active ?
948794c9bbcSBrian Somers              dl->bundle->ncp.mp.server.socket.sun_path : "");
949794c9bbcSBrian Somers 
950794c9bbcSBrian Somers     /* Fetch IP, netmask from IPCP */
951794c9bbcSBrian Somers     memcpy(&ac->ip, peer_ip, sizeof(ac->ip));
952794c9bbcSBrian Somers     memcpy(&ac->mask, netmask, sizeof(ac->mask));
953794c9bbcSBrian Somers   };
954794c9bbcSBrian Somers 
955794c9bbcSBrian Somers   if (rad_put_string(r->cx.rad, RAD_USER_NAME, ac->user_name) != 0 ||
956794c9bbcSBrian Somers       rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
957794c9bbcSBrian Somers       rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0 ||
958794c9bbcSBrian Somers       rad_put_addr(r->cx.rad, RAD_FRAMED_IP_ADDRESS, ac->ip) != 0 ||
959794c9bbcSBrian Somers       rad_put_addr(r->cx.rad, RAD_FRAMED_IP_NETMASK, ac->mask) != 0) {
960794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
961794c9bbcSBrian Somers     rad_close(r->cx.rad);
962794c9bbcSBrian Somers     return;
963794c9bbcSBrian Somers   }
964794c9bbcSBrian Somers 
965794c9bbcSBrian Somers   if (gethostname(hostname, sizeof hostname) != 0)
966794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
967794c9bbcSBrian Somers   else {
968aadbb4eaSBrian Somers #if 0
969794c9bbcSBrian Somers     if ((hp = gethostbyname(hostname)) != NULL) {
970794c9bbcSBrian Somers       hostaddr.s_addr = *(u_long *)hp->h_addr;
971794c9bbcSBrian Somers       if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
972794c9bbcSBrian Somers         log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
973794c9bbcSBrian Somers                    rad_strerror(r->cx.rad));
974794c9bbcSBrian Somers         rad_close(r->cx.rad);
975794c9bbcSBrian Somers         return;
976794c9bbcSBrian Somers       }
977794c9bbcSBrian Somers     }
978aadbb4eaSBrian Somers #endif
979794c9bbcSBrian Somers     if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
980794c9bbcSBrian Somers       log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
981794c9bbcSBrian Somers                  rad_strerror(r->cx.rad));
982794c9bbcSBrian Somers       rad_close(r->cx.rad);
983794c9bbcSBrian Somers       return;
984794c9bbcSBrian Somers     }
985794c9bbcSBrian Somers   }
986794c9bbcSBrian Somers 
987de59e178SBrian Somers   radius_put_physical_details(r->cx.rad, dl->physical);
988794c9bbcSBrian Somers 
989794c9bbcSBrian Somers   if (rad_put_int(r->cx.rad, RAD_ACCT_STATUS_TYPE, acct_type) != 0 ||
990794c9bbcSBrian Somers       rad_put_string(r->cx.rad, RAD_ACCT_SESSION_ID, ac->session_id) != 0 ||
991794c9bbcSBrian Somers       rad_put_string(r->cx.rad, RAD_ACCT_MULTI_SESSION_ID,
992794c9bbcSBrian Somers                      ac->multi_session_id) != 0 ||
993794c9bbcSBrian Somers       rad_put_int(r->cx.rad, RAD_ACCT_DELAY_TIME, 0) != 0) {
994794c9bbcSBrian Somers /* XXX ACCT_DELAY_TIME should be increased each time a packet is waiting */
995794c9bbcSBrian Somers     log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
996794c9bbcSBrian Somers     rad_close(r->cx.rad);
997794c9bbcSBrian Somers     return;
998794c9bbcSBrian Somers   }
999794c9bbcSBrian Somers 
1000794c9bbcSBrian Somers   if (acct_type == RAD_STOP)
1001794c9bbcSBrian Somers   /* Show some statistics */
1002794c9bbcSBrian Somers     if (rad_put_int(r->cx.rad, RAD_ACCT_INPUT_OCTETS, stats->OctetsIn) != 0 ||
1003794c9bbcSBrian Somers         rad_put_int(r->cx.rad, RAD_ACCT_INPUT_PACKETS, stats->PacketsIn) != 0 ||
1004794c9bbcSBrian Somers         rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_OCTETS, stats->OctetsOut) != 0 ||
1005794c9bbcSBrian Somers         rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_PACKETS, stats->PacketsOut)
1006794c9bbcSBrian Somers         != 0 ||
1007794c9bbcSBrian Somers         rad_put_int(r->cx.rad, RAD_ACCT_SESSION_TIME, throughput_uptime(stats))
1008794c9bbcSBrian Somers         != 0) {
1009794c9bbcSBrian Somers       log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
1010794c9bbcSBrian Somers       rad_close(r->cx.rad);
1011794c9bbcSBrian Somers       return;
1012794c9bbcSBrian Somers     }
1013794c9bbcSBrian Somers 
1014c42627ffSBrian Somers   r->cx.auth = NULL;			/* Not valid for accounting requests */
1015794c9bbcSBrian Somers   if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
1016794c9bbcSBrian Somers     radius_Process(r, got);
1017794c9bbcSBrian Somers   else {
1018794c9bbcSBrian Somers     log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
1019794c9bbcSBrian Somers     r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
1020794c9bbcSBrian Somers     r->cx.timer.func = radius_Timeout;
1021c42627ffSBrian Somers     r->cx.timer.name = "radius acct";
1022794c9bbcSBrian Somers     r->cx.timer.arg = r;
1023794c9bbcSBrian Somers     timer_Start(&r->cx.timer);
1024794c9bbcSBrian Somers   }
1025794c9bbcSBrian Somers }
1026794c9bbcSBrian Somers 
1027794c9bbcSBrian Somers /*
1028f0cdd9c0SBrian Somers  * How do things look at the moment ?
1029f0cdd9c0SBrian Somers  */
1030972a1bcfSBrian Somers void
1031972a1bcfSBrian Somers radius_Show(struct radius *r, struct prompt *p)
1032972a1bcfSBrian Somers {
103374457d3dSBrian Somers   prompt_Printf(p, " Radius config:     %s",
103474457d3dSBrian Somers                 *r->cfg.file ? r->cfg.file : "none");
1035972a1bcfSBrian Somers   if (r->valid) {
1036972a1bcfSBrian Somers     prompt_Printf(p, "\n                IP: %s\n", inet_ntoa(r->ip));
1037972a1bcfSBrian Somers     prompt_Printf(p, "           Netmask: %s\n", inet_ntoa(r->mask));
1038972a1bcfSBrian Somers     prompt_Printf(p, "               MTU: %lu\n", r->mtu);
1039972a1bcfSBrian Somers     prompt_Printf(p, "                VJ: %sabled\n", r->vj ? "en" : "dis");
1040ff8e577bSBrian Somers     prompt_Printf(p, "           Message: %s\n", r->repstr ? r->repstr : "");
10418fb5ef5aSBrian Somers     prompt_Printf(p, "   MPPE Enc Policy: %s\n",
10428fb5ef5aSBrian Somers                   radius_policyname(r->mppe.policy));
10438fb5ef5aSBrian Somers     prompt_Printf(p, "    MPPE Enc Types: %s\n",
10448fb5ef5aSBrian Somers                   radius_typesname(r->mppe.types));
10458fb5ef5aSBrian Somers     prompt_Printf(p, "     MPPE Recv Key: %seceived\n",
10468fb5ef5aSBrian Somers                   r->mppe.recvkey ? "R" : "Not r");
10478fb5ef5aSBrian Somers     prompt_Printf(p, "     MPPE Send Key: %seceived\n",
10488fb5ef5aSBrian Somers                   r->mppe.sendkey ? "R" : "Not r");
1049a16061b2SBrian Somers     prompt_Printf(p, " MS-CHAP2-Response: %s\n",
1050a16061b2SBrian Somers                   r->msrepstr ? r->msrepstr : "");
1051ff8e577bSBrian Somers     prompt_Printf(p, "     Error Message: %s\n", r->errstr ? r->errstr : "");
1052972a1bcfSBrian Somers     if (r->routes)
1053972a1bcfSBrian Somers       route_ShowSticky(p, r->routes, "            Routes", 16);
1054972a1bcfSBrian Somers   } else
1055972a1bcfSBrian Somers     prompt_Printf(p, " (not authenticated)\n");
1056972a1bcfSBrian Somers }
1057