]> git.ozlabs.org Git - ppp.git/blob - pppd/plugins/radius/radius.c
Added radius_pre_auth_hook to let additional plugins stacked on top
[ppp.git] / pppd / plugins / radius / radius.c
1 /***********************************************************************
2 *
3 * radius.c
4 *
5 * RADIUS plugin for pppd.  Performs PAP and CHAP authentication using
6 * RADIUS.
7 *
8 * Copyright (C) 2002 Roaring Penguin Software Inc.
9 *
10 * Based on a patch for ipppd, which is:
11 *    Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
12 *    Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
13 *    Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
14 *
15 * Uses radiusclient library, which is:
16 *    Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
17 *    Copyright (C) 2002 Roaring Penguin Software Inc.
18 *
19 * This plugin may be distributed according to the terms of the GNU
20 * General Public License, version 2 or (at your option) any later version.
21 *
22 ***********************************************************************/
23 static char const RCSID[] =
24 "$Id: radius.c,v 1.2 2002/02/08 17:28:31 dfs Exp $";
25
26 #include "pppd.h"
27 #include "chap.h"
28 #include "radiusclient.h"
29 #include "fsm.h"
30 #include "ipcp.h"
31 #include <syslog.h>
32 #include <sys/types.h>
33 #include <sys/time.h>
34
35 #define BUF_LEN 1024
36
37 static char *config_file = NULL;
38
39 static option_t Options[] = {
40     { "radius-config-file", o_string, &config_file },
41     { NULL }
42 };
43
44 static int radius_secret_check(void);
45 static int radius_pap_auth(char *user,
46                            char *passwd,
47                            char **msgp,
48                            struct wordlist **paddrs,
49                            struct wordlist **popts);
50 static int radius_chap_auth(char *user,
51                             u_char *remmd,
52                             int remmd_len,
53                             chap_state *cstate);
54
55 static void radius_ip_up(void *opaque, int arg);
56 static void radius_ip_down(void *opaque, int arg);
57 static void make_username_realm(char *user);
58 static int radius_setparams(VALUE_PAIR *vp, char *msg);
59 static void radius_choose_ip(u_int32_t *addrp);
60 static int radius_init(char *msg);
61 static int get_client_port(char *ifname);
62 static int radius_allowed_address(u_int32_t addr);
63
64 void (*radius_attributes_hook)(VALUE_PAIR *) = NULL;
65 void (*radius_pre_auth_hook)(char const *user) = NULL;
66
67 #ifndef MAXSESSIONID
68 #define MAXSESSIONID 32
69 #endif
70
71 struct radius_state {
72     int accounting_started;
73     int initialized;
74     int client_port;
75     int choose_ip;
76     int any_ip_addr_ok;
77     int done_chap_once;
78     u_int32_t ip_addr;
79     char user[MAXNAMELEN];
80     char config_file[MAXPATHLEN];
81     char session_id[MAXSESSIONID + 1];
82     time_t start_time;
83 };
84
85 static struct radius_state rstate;
86
87 char pppd_version[] = VERSION;
88
89 /**********************************************************************
90 * %FUNCTION: plugin_init
91 * %ARGUMENTS:
92 *  None
93 * %RETURNS:
94 *  Nothing
95 * %DESCRIPTION:
96 *  Initializes RADIUS plugin.
97 ***********************************************************************/
98 void
99 plugin_init(void)
100 {
101     pap_check_hook = radius_secret_check;
102     pap_auth_hook = radius_pap_auth;
103
104     chap_check_hook = radius_secret_check;
105     chap_auth_hook = radius_chap_auth;
106
107     ip_choose_hook = radius_choose_ip;
108     allowed_address_hook = radius_allowed_address;
109
110     add_notifier(&ip_up_notifier, radius_ip_up, NULL);
111     add_notifier(&ip_down_notifier, radius_ip_down, NULL);
112
113     memset(&rstate, 0, sizeof(rstate));
114
115     strlcpy(rstate.config_file, "/etc/radiusclient/radiusclient.conf",
116             sizeof(rstate.config_file));
117
118     add_options(Options);
119
120     info("RADIUS plugin initialized.");
121 }
122
123 /**********************************************************************
124 * %FUNCTION: radius_secret_check
125 * %ARGUMENTS:
126 *  None
127 * %RETURNS:
128 *  1 -- we are ALWAYS willing to supply a secret. :-)
129 * %DESCRIPTION:
130 * Tells pppd that we will try to authenticate the peer, and not to
131 * worry about looking in /etc/ppp/*-secrets
132 ***********************************************************************/
133 static int
134 radius_secret_check(void)
135 {
136     return 1;
137 }
138
139 /**********************************************************************
140 * %FUNCTION: radius_choose_ip
141 * %ARGUMENTS:
142 *  addrp -- where to store the IP address
143 * %RETURNS:
144 *  Nothing
145 * %DESCRIPTION:
146 *  If RADIUS server has specified an IP address, it is stored in *addrp.
147 ***********************************************************************/
148 static void
149 radius_choose_ip(u_int32_t *addrp)
150 {
151     if (rstate.choose_ip) {
152         *addrp = rstate.ip_addr;
153     }
154 }
155
156 /**********************************************************************
157 * %FUNCTION: radius_pap_auth
158 * %ARGUMENTS:
159 *  user -- user-name of peer
160 *  passwd -- password supplied by peer
161 *  msgp -- Message which will be sent in PAP response
162 *  paddrs -- set to a list of possible peer IP addresses
163 *  popts -- set to a list of additional pppd options
164 * %RETURNS:
165 *  1 if we can authenticate, -1 if we cannot.
166 * %DESCRIPTION:
167 * Performs PAP authentication using RADIUS
168 ***********************************************************************/
169 static int
170 radius_pap_auth(char *user,
171                 char *passwd,
172                 char **msgp,
173                 struct wordlist **paddrs,
174                 struct wordlist **popts)
175 {
176     VALUE_PAIR *send, *received;
177     UINT4 av_type;
178     int result;
179     static char radius_msg[BUF_LEN];
180
181     radius_msg[0] = 0;
182     *msgp = radius_msg;
183
184     if (radius_init(radius_msg) < 0) {
185         return 0;
186     }
187
188     /* Put user with potentially realm added in rstate.user */
189     make_username_realm(user);
190
191     if (radius_pre_auth_hook) {
192         radius_pre_auth_hook(rstate.user);
193     }
194
195     send = NULL;
196     received = NULL;
197
198     /* Hack... the "port" is the ppp interface number.  Should really be
199        the tty */
200     rstate.client_port = get_client_port(ifname);
201
202     av_type = PW_FRAMED;
203     rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
204
205     av_type = PW_PPP;
206     rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
207
208     rc_avpair_add(&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE);
209     rc_avpair_add(&send, PW_USER_PASSWORD, passwd, 0, VENDOR_NONE);
210     if (*remote_number) {
211         rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0,
212                        VENDOR_NONE);
213     }
214
215     result = rc_auth(rstate.client_port, send, &received, radius_msg);
216
217     if (result == OK_RC) {
218         if (radius_setparams(received, radius_msg) < 0) {
219             result = ERROR_RC;
220         }
221     }
222
223     /* free value pairs */
224     rc_avpair_free(received);
225     rc_avpair_free(send);
226
227     return (result == OK_RC) ? 1 : 0;
228 }
229
230 /**********************************************************************
231 * %FUNCTION: radius_chap_auth
232 * %ARGUMENTS:
233 *  user -- user-name of peer
234 *  remmd -- hash received from peer
235 *  remmd_len -- length of remmd
236 *  cstate -- pppd's chap_state structure
237 * %RETURNS:
238 *  CHAP_SUCCESS if we can authenticate, CHAP_FAILURE if we cannot.
239 * %DESCRIPTION:
240 * Performs CHAP authentication using RADIUS
241 ***********************************************************************/
242 static int
243 radius_chap_auth(char *user,
244                  u_char *remmd,
245                  int remmd_len,
246                  chap_state *cstate)
247 {
248     VALUE_PAIR *send, *received;
249     UINT4 av_type;
250     static char radius_msg[BUF_LEN];
251     int result;
252     u_char cpassword[MD5_SIGNATURE_SIZE+1];
253     radius_msg[0] = 0;
254
255     if (radius_init(radius_msg) < 0) {
256         error("%s", radius_msg);
257         return CHAP_FAILURE;
258     }
259
260     /* we handle md5 digest at the moment */
261     if (cstate->chal_type != CHAP_DIGEST_MD5) {
262         error("RADIUS: Challenge type not MD5");
263         return CHAP_FAILURE;
264     }
265
266     /* Put user with potentially realm added in rstate.user */
267     if (!rstate.done_chap_once) {
268         make_username_realm(user);
269         rstate.client_port = get_client_port (ifname);
270         if (radius_pre_auth_hook) {
271             radius_pre_auth_hook(rstate.user);
272         }
273     }
274
275     send = received = NULL;
276
277     av_type = PW_FRAMED;
278     rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
279
280     av_type = PW_PPP;
281     rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
282
283     rc_avpair_add (&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE);
284
285     /*
286      * add the CHAP-Password and CHAP-Challenge fields
287      */
288
289     cpassword[0] = cstate->chal_id;
290
291     memcpy(&cpassword[1], remmd, MD5_SIGNATURE_SIZE);
292
293     rc_avpair_add(&send, PW_CHAP_PASSWORD, cpassword, MD5_SIGNATURE_SIZE + 1, VENDOR_NONE);
294
295     rc_avpair_add(&send, PW_CHAP_CHALLENGE, cstate->challenge, cstate->chal_len, VENDOR_NONE);
296
297     /*
298      * make authentication with RADIUS server
299      */
300
301     result = rc_auth (rstate.client_port, send, &received, radius_msg);
302
303     if (result == OK_RC) {
304         if (!rstate.done_chap_once) {
305             if (radius_setparams(received, radius_msg) < 0) {
306                 error("%s", radius_msg);
307                 result = ERROR_RC;
308             } else {
309                 rstate.done_chap_once = 1;
310             }
311         }
312     }
313
314     rc_avpair_free(received);
315     rc_avpair_free (send);
316     return (result == OK_RC) ? CHAP_SUCCESS : CHAP_FAILURE;
317 }
318
319 /**********************************************************************
320 * %FUNCTION: make_username_realm
321 * %ARGUMENTS:
322 *  user -- the user given to pppd
323 * %RETURNS:
324 *  Nothing
325 * %DESCRIPTION:
326 *  Copies user into rstate.user.  If it lacks a realm (no "@domain" part),
327 * then the default realm from the radiusclient config file is added.
328 ***********************************************************************/
329 static void
330 make_username_realm(char *user)
331 {
332     char *default_realm;
333
334     if ( user != NULL ) {
335         strlcpy(rstate.user, user, sizeof(rstate.user));
336     }  else {
337         rstate.user[0] = 0;
338     }
339
340     default_realm = rc_conf_str("default_realm");
341
342     if (!strchr(rstate.user, '@') &&
343         default_realm &&
344         (*default_realm != '\0')) {
345         strlcat(rstate.user, "@", sizeof(rstate.user));
346         strlcat(rstate.user, default_realm, sizeof(rstate.user));
347     }
348 }
349
350 /**********************************************************************
351 * %FUNCTION: radius_setparams
352 * %ARGUMENTS:
353 *  vp -- received value-pairs
354 *  msg -- buffer in which to place error message.  Holds up to BUF_LEN chars
355 * %RETURNS:
356 *  >= 0 on success; -1 on failure
357 * %DESCRIPTION:
358 *  Parses attributes sent by RADIUS server and sets them in pppd.  Currently,
359 *  used only to set IP address.
360 ***********************************************************************/
361 static int
362 radius_setparams(VALUE_PAIR *vp, char *msg)
363 {
364     u_int32_t remote;
365
366     /* Send RADIUS attributes to anyone else who might be interested */
367     if (radius_attributes_hook) {
368         (*radius_attributes_hook)(vp);
369     }
370
371     /*
372      * service type (if not framed then quit),
373      * new IP address (RADIUS can define static IP for some users),
374      */
375
376     while (vp) {
377         switch (vp->attribute) {
378         case PW_SERVICE_TYPE:
379             /* check for service type       */
380             /* if not FRAMED then exit      */
381             if (vp->lvalue != PW_FRAMED) {
382                 slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
383                          vp->lvalue, rstate.user);
384                 return -1;
385             }
386             break;
387         case PW_FRAMED_PROTOCOL:
388             /* check for framed protocol type       */
389             /* if not PPP then also exit            */
390             if (vp->lvalue != PW_PPP) {
391                 slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
392                          vp->lvalue, rstate.user);
393                 return -1;
394             }
395             break;
396
397         case PW_FRAMED_IP_ADDRESS:
398             /* seting up remote IP addresses */
399             remote = vp->lvalue;
400             if (remote == 0xffffffff) {
401                 /* 0xffffffff means user should be allowed to select one */
402                 rstate.any_ip_addr_ok = 1;
403             } else if (remote != 0xfffffffe) {
404                 /* 0xfffffffe means NAS should select an ip address */
405                 remote = htonl(vp->lvalue);
406                 if (bad_ip_adrs (remote)) {
407                     slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
408                              remote, rstate.user);
409                     return -1;
410                 }
411                 rstate.choose_ip = 1;
412                 rstate.ip_addr = remote;
413             }
414             break;
415         }
416         vp = vp->next;
417     }
418     return 0;
419 }
420
421 /**********************************************************************
422 * %FUNCTION: radius_acct_start
423 * %ARGUMENTS:
424 *  None
425 * %RETURNS:
426 *  Nothing
427 * %DESCRIPTION:
428 *  Sends a "start" accounting message to the RADIUS server.
429 ***********************************************************************/
430 static void
431 radius_acct_start(void)
432 {
433     UINT4 av_type;
434     int result;
435     VALUE_PAIR *send = NULL;
436     ipcp_options *ho = &ipcp_hisoptions[0];
437     u_int32_t hisaddr;
438
439     if (!rstate.initialized) {
440         return;
441     }
442
443     rstate.start_time = time(NULL);
444
445     strncpy(rstate.session_id, rc_mksid(), sizeof(rstate.session_id));
446
447     rc_avpair_add(&send, PW_ACCT_SESSION_ID,
448                    rstate.session_id, 0, VENDOR_NONE);
449     rc_avpair_add(&send, PW_USER_NAME,
450                    rstate.user, 0, VENDOR_NONE);
451
452     av_type = PW_STATUS_START;
453     rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
454
455     av_type = PW_FRAMED;
456     rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
457
458     av_type = PW_PPP;
459     rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
460
461     if (*remote_number) {
462         rc_avpair_add(&send, PW_CALLING_STATION_ID,
463                        remote_number, 0, VENDOR_NONE);
464     }
465
466     av_type = PW_RADIUS;
467     rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
468
469
470     av_type = PW_ASYNC;
471     rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
472
473     hisaddr = ho->hisaddr;
474     av_type = htonl(hisaddr);
475     rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
476
477     result = rc_acct(rstate.client_port, send);
478
479     rc_avpair_free(send);
480
481     if (result != OK_RC) {
482         /* RADIUS server could be down so make this a warning */
483         syslog(LOG_WARNING,
484                 "Accounting START failed for %s", rstate.user);
485     } else {
486         rstate.accounting_started = 1;
487     }
488 }
489
490 /**********************************************************************
491 * %FUNCTION: radius_acct_stop
492 * %ARGUMENTS:
493 *  None
494 * %RETURNS:
495 *  Nothing
496 * %DESCRIPTION:
497 *  Sends a "stop" accounting message to the RADIUS server.
498 ***********************************************************************/
499 static void
500 radius_acct_stop(void)
501 {
502     UINT4 av_type;
503     VALUE_PAIR *send = NULL;
504     ipcp_options *ho = &ipcp_hisoptions[0];
505     u_int32_t hisaddr;
506     int result;
507
508     if (!rstate.initialized) {
509         return;
510     }
511
512     if (!rstate.accounting_started) {
513         return;
514     }
515
516     rstate.accounting_started = 0;
517     rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id,
518                    0, VENDOR_NONE);
519
520     rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE);
521
522     av_type = PW_STATUS_STOP;
523     rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
524
525     av_type = PW_FRAMED;
526     rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
527
528     av_type = PW_PPP;
529     rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
530
531     av_type = PW_RADIUS;
532     rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
533
534
535     if (link_stats_valid) {
536         av_type = link_connect_time;
537         rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE);
538
539         av_type = link_stats.bytes_out;
540         rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE);
541
542         av_type = link_stats.bytes_in;
543         rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE);
544
545         av_type = link_stats.pkts_out;
546         rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE);
547
548         av_type = link_stats.pkts_in;
549         rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE);
550     }
551
552     if (*remote_number) {
553         rc_avpair_add(&send, PW_CALLING_STATION_ID,
554                        remote_number, 0, VENDOR_NONE);
555     }
556
557     av_type = PW_ASYNC;
558     rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
559
560     hisaddr = ho->hisaddr;
561     av_type = htonl(hisaddr);
562     rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
563
564     result = rc_acct(rstate.client_port, send);
565     if (result != OK_RC) {
566         /* RADIUS server could be down so make this a warning */
567         syslog(LOG_WARNING,
568                 "Accounting STOP failed for %s", rstate.user);
569     }
570     rc_avpair_free(send);
571 }
572
573 /**********************************************************************
574 * %FUNCTION: radius_ip_up
575 * %ARGUMENTS:
576 *  opaque -- ignored
577 *  arg -- ignored
578 * %RETURNS:
579 *  Nothing
580 * %DESCRIPTION:
581 *  Called when IPCP is up.  We'll do a start-accounting record.
582 ***********************************************************************/
583 static void
584 radius_ip_up(void *opaque, int arg)
585 {
586     radius_acct_start();
587 }
588
589 /**********************************************************************
590 * %FUNCTION: radius_ip_down
591 * %ARGUMENTS:
592 *  opaque -- ignored
593 *  arg -- ignored
594 * %RETURNS:
595 *  Nothing
596 * %DESCRIPTION:
597 *  Called when IPCP is down.  We'll do a stop-accounting record.
598 ***********************************************************************/
599 static void
600 radius_ip_down(void *opaque, int arg)
601 {
602     radius_acct_stop();
603 }
604
605 /**********************************************************************
606 * %FUNCTION: radius_init
607 * %ARGUMENTS:
608 *  msg -- buffer of size BUF_LEN for error message
609 * %RETURNS:
610 *  negative on failure; non-negative on success
611 * %DESCRIPTION:
612 *  Initializes radiusclient library
613 ***********************************************************************/
614 static int
615 radius_init(char *msg)
616 {
617     if (rstate.initialized) {
618         return 0;
619     }
620
621     if (config_file && *config_file) {
622         strlcpy(rstate.config_file, config_file, MAXPATHLEN-1);
623     }
624
625     rstate.initialized = 1;
626
627     if (rc_read_config(rstate.config_file) != 0) {
628         slprintf(msg, BUF_LEN, "RADIUS: Can't read config file %s",
629                  rstate.config_file);
630         return -1;
631     }
632
633     if (rc_read_dictionary(rc_conf_str("dictionary")) != 0) {
634         slprintf(msg, BUF_LEN, "RADIUS: Can't read dictionary file %s",
635                  rc_conf_str("dictionary"));
636         return -1;
637     }
638
639     if (rc_read_mapfile(rc_conf_str("mapfile")) != 0)   {
640         slprintf(msg, BUF_LEN, "RADIUS: Can't read map file %s",
641                  rc_conf_str("mapfile"));
642         return -1;
643     }
644     return 0;
645 }
646
647 /**********************************************************************
648 * %FUNCTION: get_client_port
649 * %ARGUMENTS:
650 *  ifname -- PPP interface name (e.g. "ppp7")
651 * %RETURNS:
652 *  The NAS port number (e.g. 7)
653 * %DESCRIPTION:
654 *  Extracts the port number from the interface name
655 ***********************************************************************/
656 static int
657 get_client_port(char *ifname)
658 {
659     int port;
660     if (sscanf(ifname, "ppp%d", &port) == 1) {
661         return port;
662     }
663     return rc_map2id(ifname);
664 }
665
666 /**********************************************************************
667 * %FUNCTION: radius_allowed_address
668 * %ARGUMENTS:
669 *  addr -- IP address
670 * %RETURNS:
671 *  1 if we're allowed to use that IP address; 0 if not; -1 if we do
672 *  not know.
673 ***********************************************************************/
674 static int
675 radius_allowed_address(u_int32_t addr)
676 {
677     ipcp_options *wo = &ipcp_wantoptions[0];
678
679     if (!rstate.choose_ip) {
680         /* If RADIUS server said any address is OK, then fine... */
681         if (rstate.any_ip_addr_ok) {
682             return 1;
683         }
684
685         /* Sigh... if an address was supplied for remote host in pppd
686            options, it has to match that.  */
687         if (wo->hisaddr != 0 && wo->hisaddr == addr) {
688             return 1;
689         }
690
691         return 0;
692     }
693     if (addr == rstate.ip_addr) return 1;
694     return 0;
695 }
696
697 /* Useful for other plugins */
698 char *radius_logged_in_user(void)
699 {
700     return rstate.user;
701 }
702