/* * $Id$ * * Copyright (C) 1996, Matjaz Godec * Copyright (C) 1996, Lars Fenneberg * */ #include #include #include #include #include #include #include #include #include #include "ipppd.h" #include "fsm.h" #include "lcp.h" #include "upap.h" #include "chap.h" #include "ipcp.h" #include "ccp.h" #include "pathnames.h" char *ip_ntoa __P((u_int32_t)); int bad_ip_adrs __P((u_int32_t)); char username_realm[255]; char radius_user[MAXNAMELEN]; char *make_username_realm ( char * ); static int client_port; int called_radius_init = 0; int auth_order = 0 ; u_int32_t default_hisaddr = 0 ; u_int32_t default_ouraddr = 0 ; u_int32_t default_netmask = 0 ; extern int idle_time_limit ; extern int session_time_limit ; void RestartIdleTimer __P((fsm *)); void RestartSessionTimer __P((fsm *)); struct ifstats { long rx_bytes; long rx_packets; long tx_bytes; long tx_packets; }; ENV *env = NULL; char **environment; #ifndef ENV_SIZE #define ENV_SIZE 128 #endif /*************************************************************************** * * Name: radius_init * * Purpose: Initializing radiusclient * ***************************************************************************/ int radius_init() { static char *func = "radius_init" ; syslog(LOG_DEBUG, "%s: entered", func ) ; if (rc_read_config (PATH_RADIUSCLIENT_CONF) != 0) { syslog(LOG_ERR, "can't load config file %s in %s", PATH_RADIUSCLIENT_CONF, func ) ; return (-1) ; } ; if (rc_read_dictionary (rc_conf_str ("dictionary")) != 0) { syslog(LOG_ERR , "can't load dictionary file %s in %s" , rc_conf_str ("dictionary") , func ) ; return (-1); } ; if (rc_read_mapfile (rc_conf_str ("mapfile")) != 0) { syslog(LOG_ERR , "can't load map file %s in %s" , rc_conf_str ("mapfile") , func ) ; return (-1); } ; called_radius_init = 1; return 0; } /*************************************************************************** * * Name: setparams * * Purpose: Set's up pppd parameters as received from RADIUS server * ***************************************************************************/ static int radius_setparams(unit,vp) int unit; VALUE_PAIR *vp; { fsm *f = &lcp_fsm[unit]; ipcp_options *wo = &ipcp_wantoptions[unit]; u_int32_t remote = 0; static char *func = "radius_setparams" ; syslog(LOG_DEBUG, "%s: entered", func ) ; /* * service type (if not framed then quit), * new IP address (RADIUS can define static IP for some users), * new netmask (RADIUS can define netmask), * idle time limit ( RADIUS can define idle timeout), * session time limit ( RADIUS can limit session time ), */ while (vp) { switch (vp->attribute) { case PW_SERVICE_TYPE: /* check for service type */ /* if not FRAMED then exit */ if (vp->lvalue != PW_FRAMED) { syslog (LOG_NOTICE, "RADIUS wrong service type %ld for %s", vp->lvalue, radius_user ); return (-1); } break; case PW_FRAMED_PROTOCOL: /* check for framed protocol type */ /* if not PPP then also exit */ if (vp->lvalue != PW_PPP) { syslog (LOG_NOTICE, "RADIUS wrong framed protocol %ld for %s)", vp->lvalue, radius_user ); return (-1); } break; case PW_FRAMED_IP_ADDRESS: /* seting up static IP addresses */ /* 0xfffffffe means NAS should select an ip address */ /* 0xffffffff means user should be allowed to select one */ /* the last case probably needs special handling ??? */ remote = vp->lvalue; if ((remote != 0xfffffffe) && (remote != 0xffffffff)) { remote = htonl(remote); if (bad_ip_adrs (remote)) { syslog (LOG_ERR, "RADIUS bad remote IP address %s for %s in %s", ip_ntoa (remote), radius_user, func ); return (-1); } wo->hisaddr = remote; syslog (LOG_DEBUG, "Assigned remote static IP %s in %s", ip_ntoa (remote), func ) ; } break; case PW_FRAMED_IP_NETMASK: /* changing netmask has some problems too */ /* Boy have I looked when I was changed */ /* server's config for USR/TC and none of my */ /* linux TS didn't work any more :( */ netmask = htonl (vp->lvalue); syslog (LOG_DEBUG, "Assigned netmask %s in %s", ip_ntoa (netmask), func ) ; break; case PW_FRAMED_MTU: /* Don't know if this is OK but what the hack */ /* anyone using this succesfully ? */ lcp_allowoptions[unit].mru = vp->lvalue; syslog (LOG_DEBUG, "Assigned mtu %ld in %s" , vp->lvalue , func ) ; break; case PW_IDLE_TIMEOUT: /* This one is operational */ /* have using it for some time */ idle_time_limit = vp->lvalue; if (idle_time_limit != 0) { RestartIdleTimer ( f ); syslog (LOG_DEBUG, "Assigned idle timeout %ld in %s" , vp->lvalue , func ) ; } break; case PW_SESSION_TIMEOUT: /* This one works also for me */ session_time_limit = vp->lvalue; if ( session_time_limit != 0 ) { RestartSessionTimer ( f ); syslog (LOG_DEBUG, "assigned session timeout %ld in %s" , session_time_limit, func ) ; } break; case PW_FILTER_ID: /* Idea for future implementation */ /* if we get the name of the filter */ /* we can run ipfwadm based script */ /* and have one new functione as the */ /* big boys have. Any volunteers ? */ /* I would look at ip-up imeplementation*/ /* and copied it here */ syslog (LOG_NOTICE, "Dynamic filtering not implemented yet in %s", func ); break; case PW_FRAMED_ROUTING: /* Idea for future implementation */ /* Have no idea how to implement */ /* for those who runs gated routing */ /* would go by default if i'm not wrong */ syslog (LOG_NOTICE, "Dynamic routing setup not implemented yet in %s", func ); break; } vp = vp->next; } return 0; } #ifdef RADIUS_WTMP_LOGGING /*************************************************************************** * * Name: radius_wtmp_logging * * Purpose: write user into wtmp database * ***************************************************************************/ static void radius_wtmp_logging(user,unit) char *user; int unit ; { char *tty; static char *func = "radius_wtmp_logging" ; syslog(LOG_DEBUG, "%s: entered", func ) ; syslog(LOG_DEBUG, "user %s logged in", user); tty = lns[unit].devnam; if (strncmp(tty, "/dev/", 5) == 0) { tty += 5; } logwtmputmp(unit, tty, radius_user, ""); lns[unit].logged_in = TRUE; } #endif /*************************************************************************** * * Name: radius_buildenv * * Purpose: * ***************************************************************************/ int radius_buildenv(env, vp) ENV *env ; VALUE_PAIR *vp ; { char name[2048] ; char value[2048]; /* more than enough */ char *p; int acount[256]; int attr; rc_add_env(env, "RADIUS_USER_NAME", radius_user); while (vp) { strcpy(name, "RADIUS_"); if (rc_avpair_tostr(vp, name+7, sizeof(name)-7, value, sizeof(value)) < 0) { return 1; } /* Translate "-" => "_" and uppercase*/ for(p = name; *p; p++) { *p = toupper(*p); if (*p == '-') *p = '_'; } /* Add to the attribute count and append the var if necessary. */ if ((attr = vp->attribute) < 256) { int count; if ((count = acount[attr]++) > 0) { char buf[10]; sprintf(buf, "_%d", count); strcat(name,buf); } } if (rc_add_env(env, name, value) < 0) { return 1; } vp = vp->next; } return 0; } /**************************************************************************** * * Name: radius_pap_auth * * Purpose: Check the user name and password against RADIUS server * and add accounting start record of the user if OK. * * Returns: UPAP_AUTHNAK: Login failed. * UPAP_AUTHACK: Login succeeded. * ****************************************************************************/ int radius_pap_auth (unit, user, passwd, msg, msglen ) int unit ; char *user; char *passwd; char **msg; int *msglen; { VALUE_PAIR *send ; VALUE_PAIR *received; UINT4 av_type; static char radius_msg[4096]; int result; static char *func = "radius_pap_auth" ; syslog(LOG_DEBUG, "%s: entered", func ) ; send = NULL ; received = NULL; client_port = rc_map2id (lns[unit].devnam); av_type = PW_FRAMED; rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0); av_type = PW_PPP; rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0); strncpy ( radius_user , make_username_realm ( user ) , sizeof (radius_user)); rc_avpair_add (&send, PW_USER_NAME, radius_user , 0); rc_avpair_add (&send, PW_USER_PASSWORD, passwd, 0); result = rc_auth (client_port, send, &received, radius_msg); if (result == OK_RC) { if (radius_setparams(unit,received) < 0) { syslog (LOG_ERR,"Error setting params in %s" , func ); result = ERROR_RC; } else { /* Build the environment for ip-up and ip-down */ if ( env != NULL ) { rc_free_env ( env ) ; } ; env = rc_new_env(ENV_SIZE); if (env != NULL) { if (radius_buildenv(env, received)) env = NULL; if (env != NULL) environment = env->env; } } } else { syslog (LOG_ERR,"Error sending auth request in %s" , func ); } rc_avpair_free(received); rc_avpair_free (send); *msg = radius_msg; *msglen = strlen(radius_msg); #ifdef RADIUS_WTMP_LOGGING if (result == OK_RC) { radius_wtmp_logging(user,unit); } #endif return (result == OK_RC)?UPAP_AUTHACK:UPAP_AUTHNAK; } /*************************************************************************** * * Name: radius_chap_auth * * Purpose: CHAP authentication with RADIUS server * ***************************************************************************/ int radius_chap_auth (unit,user, remmd, cstate ) int unit ; char *user; u_char *remmd; chap_state *cstate; { VALUE_PAIR *send; VALUE_PAIR *received; UINT4 av_type; static char radius_msg[4096]; int result; u_char cpassword[MD5_SIGNATURE_SIZE+1]; static char *func = "radius_chap_auth" ; syslog(LOG_DEBUG, "%s: entered", func ) ; /* we handle md5 digest at the moment */ if (cstate->chal_type != CHAP_DIGEST_MD5) { syslog(LOG_ERR, "Challenge type not MD5 in %s", func ) ; return(-1); } send = received = NULL; client_port = rc_map2id (lns[unit].devnam); av_type = PW_FRAMED; rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0); av_type = PW_PPP; rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0); rc_avpair_add (&send, PW_USER_NAME, radius_user , 0); /* * add the CHAP-Password and CHAP-Challenge fields */ cpassword[0] = cstate->chal_id; memcpy(&cpassword[1], remmd, MD5_SIGNATURE_SIZE); rc_avpair_add(&send, PW_CHAP_PASSWORD, cpassword, MD5_SIGNATURE_SIZE + 1); rc_avpair_add(&send, PW_CHAP_CHALLENGE, cstate->challenge, cstate->chal_len); result = rc_auth (client_port, send, &received, radius_msg); if (result == OK_RC) { if (radius_setparams(unit,received) < 0) { syslog (LOG_ERR,"Error setting params in %s" , func ); result = ERROR_RC; } } else { syslog (LOG_ERR,"Error sending auth request in %s" , func ); } rc_avpair_free(received); rc_avpair_free (send); #ifdef RADIUS_WTMP_LOGGING if (result == OK_RC) { radius_wtmp_logging(user,unit); } #endif return (result == OK_RC)?0:(-1); } /*************************************************************************** * * Name: if_getipacct * * Purpose: reads ip accounting information * ***************************************************************************/ static void if_getipacct(ifname, ifs) char *ifname ; struct ifstats *ifs ; { FILE* f = fopen("/proc/net/ip_acct","r"); char buf[256]; char acctif[128]; long dummy; long direction; long packets; long bytes; int rc; static char *func = "if_getipacct" ; syslog(LOG_DEBUG, "%s: entered", func ) ; if (!f) { syslog(LOG_ERR, "Can't open /proc/net/ip_acct in %s:" , func ) ; return; } fgets(buf, sizeof(buf), f); acctif[0] = '\0'; while (1) { rc = fscanf(f, "%ld/%ld->%ld/%ld %s %ld %ld %ld %ld %ld %ld", &dummy,&dummy,&dummy,&dummy,acctif,&dummy, &direction, &dummy, &dummy, &packets, &bytes); fgets(buf, sizeof(buf), f); if (rc == EOF) { break; } if (rc != 11) { break; } if (strcmp(ifname, acctif) == 0) { syslog(LOG_DEBUG, "Interface <%s> found in %s", acctif, func ) ; if (direction == 1000) /* incoming bytes */ { syslog(LOG_DEBUG, "Incoming bytes/packets = %ld/%ld in %s", bytes, packets, func ) ; ifs->rx_bytes = bytes; ifs->rx_packets = packets; } else { syslog(LOG_DEBUG, "Outgoing bytes/packets = %ld/%ld in %s", bytes, packets, func ) ; ifs->tx_bytes = bytes; ifs->tx_packets = packets; } } } fclose(f); } /*************************************************************************** * * Name: radius_ip_acct_on * * Purpose: set up ip accounting rules for this interface * ***************************************************************************/ static int radius_ip_acct_on ( unit ) int unit ; { static int sockfd = -1; int ret; struct ip_fw ip_acct ; static char *func = "radius_ip_acct_on" ; syslog ( LOG_DEBUG , "%s: entered" , func ) ; memset ( &ip_acct , 0x0 , sizeof ( ip_acct ) ) ; strncpy ( ip_acct.fw_vianame , lns[unit].ifname, IFNAMSIZ ) ; ip_acct.fw_flg |= IP_FW_F_ACCTIN ; if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { syslog ( LOG_ERR , "ipfwadm: socket creation failed in %s", func ); return (-1); } ret = setsockopt( sockfd, IPPROTO_IP, IP_ACCT_INSERT, &ip_acct , sizeof ( ip_acct) ); close ( sockfd ) ; if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { syslog ( LOG_ERR , "ipfwadm: socket creation failed in %s", func ); return (-1); } ip_acct.fw_flg &= ~IP_FW_F_ACCTIN ; ip_acct.fw_flg |= IP_FW_F_ACCTOUT ; ret = setsockopt( sockfd, IPPROTO_IP, IP_ACCT_INSERT, &ip_acct , sizeof ( ip_acct) ); close ( sockfd ) ; return ret; }; /*************************************************************************** * * Name: radius_ip_acct_off * * Purpose: destroy ip accounting rules for this interface * ***************************************************************************/ static int radius_ip_acct_off ( unit ) int unit ; { static int sockfd = -1; int ret; struct ip_fw ip_acct ; static char *func = "radius_ip_acct_on" ; syslog ( LOG_DEBUG , "%s: entered" , func ) ; memset ( &ip_acct , 0x0 , sizeof ( ip_acct ) ) ; strncpy ( ip_acct.fw_vianame , lns[unit].ifname, IFNAMSIZ ) ; ip_acct.fw_flg |= IP_FW_F_ACCTIN ; if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { syslog ( LOG_ERR , "ipfwadm: socket creation failed in %s", func ); return (-1); } ret = setsockopt( sockfd, IPPROTO_IP, IP_ACCT_DELETE, &ip_acct , sizeof ( ip_acct) ); close ( sockfd ) ; if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { syslog ( LOG_ERR , "ipfwadm: socket creation failed in %s", func ); return (-1); } ip_acct.fw_flg &= ~IP_FW_F_ACCTIN ; ip_acct.fw_flg |= IP_FW_F_ACCTOUT ; ret = setsockopt( sockfd, IPPROTO_IP, IP_ACCT_DELETE, &ip_acct , sizeof ( ip_acct) ); close ( sockfd ) ; return ret; }; /*************************************************************************** * * Name: radius_acct_start * * Purpose: send acct start message to RADIUS server * ***************************************************************************/ int radius_acct_start(unit) int unit ; { UINT4 av_type; int result; VALUE_PAIR *send = NULL; ipcp_options *ho = &ipcp_hisoptions[unit]; u_int32_t hisaddr ; static char *func = "radius_acct_start" ; syslog(LOG_DEBUG, "%s: entered", func ) ; client_port = rc_map2id (lns[unit].devnam); lns[unit].start_time = time (NULL); radius_ip_acct_on ( unit ) ; strncpy (lns[unit].session_id, rc_mksid (), sizeof (lns[unit].session_id) ); rc_avpair_add (&send, PW_ACCT_SESSION_ID, lns[unit].session_id, 0); rc_avpair_add (&send, PW_USER_NAME, radius_user, 0); av_type = PW_STATUS_START; rc_avpair_add (&send, PW_ACCT_STATUS_TYPE, &av_type, 0); av_type = PW_FRAMED; rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0); av_type = PW_PPP; rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0); av_type = PW_RADIUS; rc_avpair_add (&send, PW_ACCT_AUTHENTIC, &av_type, 0); rc_avpair_add (&send, PW_CALLING_STATION_ID, lns[unit].remote_number, 0); av_type = PW_ISDN_SYNC ; rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0); hisaddr = ho->hisaddr; av_type = htonl(hisaddr) ; rc_avpair_add (&send, PW_FRAMED_IP_ADDRESS , &av_type , 0 ) ; result = rc_acct (client_port, send); rc_avpair_free(send); if (result != OK_RC) { /* RADIUS server could be down so make this a warning */ syslog (LOG_WARNING, "Accounting START failed for %s in %s", radius_user,func ); } else { lns[unit].radius_in = TRUE; #ifdef RADIUS_WTMP_LOGGING if ( lns[unit].logged_in == FALSE ) radius_wtmp_logging(user,unit); #endif } return ( result ) ; } /*************************************************************************** * * Name: radius_acct_stop * * Purpose: send acct stop message to RADIUS server * ***************************************************************************/ int radius_acct_stop (unit) int unit ; { UINT4 av_type; int result; VALUE_PAIR *send = NULL; struct ifstats ifstats; ipcp_options *ho = &ipcp_hisoptions[unit]; u_int32_t hisaddr ; static char *func = "radius_acct_stop" ; syslog(LOG_DEBUG, "%s: entered", func ) ; memset ( &ifstats , 0x0 , sizeof ( ifstats )) ; if_getipacct(lns[unit].ifname, &ifstats); radius_ip_acct_off ( unit ) ; rc_avpair_add (&send, PW_ACCT_SESSION_ID, lns[unit].session_id, 0); rc_avpair_add (&send, PW_USER_NAME, radius_user, 0); av_type = PW_STATUS_STOP; rc_avpair_add (&send, PW_ACCT_STATUS_TYPE, &av_type, 0); av_type = PW_FRAMED; rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0); av_type = PW_PPP; rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0); av_type = PW_RADIUS; rc_avpair_add (&send, PW_ACCT_AUTHENTIC, &av_type, 0); av_type = time (NULL) - lns[unit].start_time; rc_avpair_add (&send, PW_ACCT_SESSION_TIME, &av_type, 0); av_type = ifstats.tx_bytes ; rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0); av_type = ifstats.rx_bytes ; rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0); av_type = ifstats.tx_packets ; rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0); av_type = ifstats.rx_packets ; rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0); rc_avpair_add (&send, PW_CALLING_STATION_ID, lns[unit].remote_number, 0); av_type = PW_ISDN_SYNC ; rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0); hisaddr = ho->hisaddr; av_type = htonl(hisaddr) ; rc_avpair_add (&send, PW_FRAMED_IP_ADDRESS , &av_type , 0 ) ; result = rc_acct (client_port, send); rc_avpair_free(send); if (result != OK_RC) { syslog(LOG_ERR, "Accounting STOP failed for %s in %s", radius_user, func); } lns[unit].radius_in = FALSE; return ( result ) ; } /*************************************************************************** * * Name: make_username_realm * * Purpose: makes username_realm from user * ***************************************************************************/ char *make_username_realm ( user ) char *user ; { char *default_realm; static char *func = "radius_acct_stop" ; syslog(LOG_DEBUG, "%s: entered", func ) ; if ( user != NULL ) { strncpy (username_realm, user,sizeof (username_realm)); } else { strncpy (username_realm, "\0" , sizeof (username_realm)); } default_realm = rc_conf_str ("default_realm"); if ( (strchr (username_realm, '@') == NULL) && default_realm && (*default_realm != '\0')) { strncat (username_realm, "@", sizeof (username_realm)); strncat (username_realm, default_realm, sizeof (username_realm)); } return ( username_realm ) ; }