/* ***************************************************************************** * * Copyright 1996, Xylogics, Inc. ALL RIGHTS RESERVED. * * ALL RIGHTS RESERVED. Licensed Material - Property of Xylogics, Inc. * This software is made available solely pursuant to the terms of a * software license agreement which governs its use. * Unauthorized duplication, distribution or sale is strictly prohibited. * * Module Description: * * RADIUS_ACCT: Host Security Server - RADIUS Support for Accounting * * Original Author: Dave Mitton Created on: April 3, 1996 * * Module Reviewers: * * lint dfox * * Revision Control Information: * * $Header: /annex_src/erpcd/radius_acct.c * * * Revision History: * * $Log: acp.c,v $ * Revision 0.1 1996/4/3 15:48:00 mitton * Initial draft * */ /* includes */ #include #include #include #include #include "../inc/config.h" #ifdef _WIN32 #include "../inc/rom/syslog.h" #else #include #include #include #include #include #include #endif #ifdef LINUX /* This is needed as else the defines in stdlib.h get into conflicts*/ #undef __OPTIMIZE__ #endif #include #ifdef LINUX #define __OPTIMIZE__ #endif #include #include #include "acp.h" #include "../libannex/api_if.h" #include "../inc/erpc/nerpcd.h" #include "../inc/port/port.h" #include "radius.h" #include "acp_regime.h" #include "acp_policy.h" #include "environment.h" #include "session_db.h" /* Code conditionals */ #define ACCT_START 1 /* Send Accounting-On and -Off messages */ /*#define DEBUG_PROTOS 1 * define debug prototype code */ /*#define SYSDEBUG 1 * define system prototypes */ /* code for setting up debugging prototypes *!* ANSI C only */ #undef _ /* also defined in nerpcd.h */ #if (__STDC__ && DEBUG_PROTOS ) #define _(x) x #else #define _(x) () #endif /* for debugging on systems without system calls prototyped */ #if (__STDC__ && SYSDEBUG) time_t time(time_t *tloc); char *inet_ntoa(struct in_addr in); void bzero(void *b, size_t len); void bcopy(void *s1, void *s2, size_t len); int syslog(int prio, char *format, ...); int printf(char *format, ...); #endif /* macros for field building routines */ #define RADIUS_BUILD_INT(bpp, type, lvalue) radius_add_attribute(bpp, type, 4, NULL, lvalue); #define RADIUS_BUILD_STR(bpp, type, ptr) radius_add_attribute(bpp, type, strlen(ptr), ptr, 0); /* Integer time subtraction */ #define TIMEDIFF(time2, time1) ((time2) - (time1)) /* structure shortcuts (from acp_regime.c) */ #define ACCT_SERVER_ADDR regime_supplement.radius_servers.acct_server.s_addr #define AUTH_SERVER_ADDR regime_supplement.radius_servers.auth_server.s_addr #ifndef UDEBUG /* Unit debugging */ /* external erpcd variables */ extern int debug; extern int raddb_up; extern char *service_name[]; /* acp_lib.c */ extern char *event_name[]; /* acp_lib.c */ extern Radius_server *default_servers; /* radius_config.c */ #define NCODETYPES 13 /*This is the number of elemnets in array below*/ extern char *codetype[]; /*This array is defined in radius_parser.c*/ /* external routines */ extern int api_open _((int pd, struct sockaddr_in *sin, char *app_nam, int sho_err)); /* api_if.c */ extern int api_bind _((int fd, int **tbp, struct sockaddr_in *sin, char *app_nam, int sho_err)); extern int api_connect _((int fd, struct sockaddr_in *sin, int pd, char *app_nam, int sho_err)); extern int api_send _((int fd, char *buf, int len, int flags, char *app_nam, int sho_err)); extern int api_recvwait _((int fd, char *buf, int len, int tmo, int flags, int sho_err, char *app_nam)); extern int api_close _((int fd)); extern int get_security_regime _((struct environment_spec *env_p)); /* acp_regime.c */ extern void release_security_regime _((struct security_regime *regimep)); extern int get_time_stamp _((struct tm *intime)); /* acp_lib.c */ extern char *port_to_name _((SECPORT *portd, char *name)); #else #include "radebug.c" /* unit debug code */ #endif /* constants */ char appname[] = "RADIUS Acct"; /* component name for error logging */ #define MAX_RAD_MSG 4096+16 /* max msg length *!* move to RADIUS.H */ /* should be max msg length + secret length */ #define VPORT_MULT 1000 /* multiplier for virtual and unknown port types */ /* local data declarations */ int actinit; /* accounting initialized */ int actfd; /* accounting socket descriptor */ struct servent *svp; /* server descriptor struct */ struct sockaddr_in actlsa; /* local socket address structure*/ struct sockaddr_in actrsa; /* remote socket address structure */ static char portname[8]; /* port name buffer */ /* current server information */ int actdelay; /* delay time */ int actretry; /* retry limit */ int acttmos; /* timeout counter */ int actbackup; /* using backup server? */ int actfailed; /* accounting service failed */ struct in_addr actbackaddr; /* backup server address */ struct in_addr actwasaddr; /* primary server addr before failover */ char actsecret[16]; /* server secret */ /* message vars */ u_char actseq; /* current accounting sequence number */ u_char *startp; /* start of request buffer */ u_char *bufp; /* working request buffer pointer */ u_char *delayp; /* delay time pointer */ time_t sndtime; /* time built to send */ u_char reqauth[16]; /* saved request authenticator */ #ifdef _WIN32 /* session records */ typedef struct sesrec { struct sesrec *next; /* next in list */ UINT32 sesid; /* session id assigned */ UINT32 nasaddr; /* NAS IP address */ SECPORT nasport; /* NAS Port descriptor */ u_char username[AUTH_STRING_LEN]; /* Username */ time_t starttime; /* starting time stamp */ struct in_addr srvaddr; /* RADIUS server IP addr */ struct in_addr ataddr; /* negoiated address */ int iservice; /* initial service type */ int actses; /* active session count */ int totses; /* total session count */ } SESREC; SESREC *seslist; /* session structure list */ #endif /* defined _WIN32 */ UINT32 nextses; /* next session id number counter */ /* Annex service type translation */ /* NOTE: not all of these services will be logged, but the table entries don't hurt */ /* this should probably be moved to somewhere visable to the services definitions */ char xa2_service_type[NSERVICES] = { -1, /* security bootup */ PW_NAS_PROMPT_USER, /* cli */ -1, /* call */ PW_LOGIN_USER, /* rlogin */ PW_LOGIN_USER, /* telnet */ PW_OUTBOUND_USER, /* ports */ -1, /* dialup address */ PW_FRAMED_USER, /* slip */ PW_FRAMED_USER, /* ppp */ PW_LOGIN_USER, /* connect */ -1, /* slip dyn-dial */ -1, /* ppp dyn-dial */ PW_CALLBACK_PROMPT_USER, /* dialback */ PW_FRAMED_USER, /* arap */ PW_ADMINISTRATIVE_USER, /* FTP */ PW_NAS_PROMPT_USER, /* cli hook */ PW_FRAMED_USER, /* ipx */ PW_DIALBACK_FRAMED_USER, /* ipx dialback */ PW_NAS_PROMPT_USER, /* rcf */ -1, /* ppp timeout */ -1, /* ppp dyn-dial timeout */ -1, /* slip timeout */ -1, /* slip dyn-dial timeout */ PW_NAS_PROMPT_USER, /* vms */ PW_FRAMED_USER, /* sync ppp */ -1, /* sync dialup */ -1, /* dyndialpass or user index */ -1, /* CHAP secret */ -1, /* CHAP good */ -1, /* CHAP bad */ -1, /* CHAP option */ -1, /* IPX dialup address */ -1, /* Output string */ -1, /* Prompt */ -1, /* Appletalk profile */ -1, /* none */ -1, /* Audit log */ -1, /* shell */ -1, /* filters */ -1, /* PRImate mgr */ -1, /* CHAP */ -1, /* MLPPP */ -1, /* modem */ -1, /* max logon */ -1, /* UDAS */ PW_FRAMED_USER /* vpn ppp */ }; /* the above table should include an entry for all defined service types */ /* local forward prototypes */ #ifdef _WIN32 int syslog( int pri, const char *format, ...); extern StructErpcdOption *ErpcdOpt; #endif int radius_send_acct_stat _((int fd, UINT32 type, UINT32 nasaddr, int cause)); int radius_send_log _((u_char *msg, int len, SESREC *sptr)); /* utility function */ char *nullchk(s) char *s; { return ((s == NULL) ? "(NULL)" : s); } /* radius_init_acnt() - Initialize RADIUS Accounting service * * one time startup init - * Init's session data block pool * Looks up RADIUS protocol port numb * Opens UDP socket using actfd * * Inputs: none * Outputs: session vars re-inited * actlsa = socket address struct filled in * fd = socket opened * * Returns: 0=success, -1 if error */ int radius_init_acnt() { /* re-init session vars */ actretry = 0; actbackup = 0; actfailed = 0; actseq = 0; actrsa.sin_family = AF_INET; /* get accounting udp port num */ svp = getservbyname("radacct", "udp"); if (svp != NULL) { actrsa.sin_port = svp->s_port; } else { actrsa.sin_port = htons(PW_ACCT_UDP_PORT); /* default rather than error out */ /* fprint (stderr, "%s: Service not defined: %s/%s\n", appname, "radacct", "udp"); exit (-1); */ } /* allocate and bind the local UDP socket */ actlsa.sin_family = AF_INET; actlsa.sin_addr.s_addr = htonl(INADDR_ANY); actlsa.sin_port = 0; if ((actfd = api_open(IPPROTO_UDP, &actlsa, appname, 1)) == -1) { syslog(LOG_ERR, "%s: Failed to open UDP socket, error %i", appname, actfd); return -1; } if (api_bind(actfd, NULL, &actlsa, appname, 1) != 0) { syslog(LOG_ERR, "%s: Failed to bind UDP socket, error %i", appname, actfd); return -1; } actinit = 1; return 0; } /* radius_open_acct() - Open socket to RADIUS Accounting Server * * Called for any possible server change or if failover to backup server * * Because the accounting server may be different for any user profile * this code only assumes that the server socket is availible but may * not be pointing at the desired server. * Since this is the case, we will not send the Open Accounting message * * Inputs: Server = address of server * bflag = 0 if new request, 1=backup failover * Output: acctfd = opened UDP socket * Start Accounting message sent to server * Returns: 0 if Success, !0 if error */ int radius_open_acct(server, nasaddr, bflag) struct in_addr server; UINT32 nasaddr; int bflag; { Radius_serverinfo *actsrvptr; int rc = 0; /* Optimization: check if current server is what we want, if so skip */ /* if we have switched to a backup server, do not got back to primary just because we started a new session */ if ((server.s_addr != actrsa.sin_addr.s_addr) && (!(actbackup && (actrsa.sin_addr.s_addr == actbackaddr.s_addr)))) { if (debug) printf("%s: Open_server [%s]\n", appname, inet_ntoa(server)); if (bflag) actwasaddr.s_addr = actrsa.sin_addr.s_addr; /* if backup, remember primary address */ actrsa.sin_addr.s_addr = server.s_addr; /* init the sockaddr structure with server addr */ /* get server info for this server address */ if ((actsrvptr = get_serverinfo(server)) == NULL) { /* failed to find, now what? */ if (debug) printf("%s: Failed to find server info for [%s]\n", appname, inet_ntoa(server)); syslog(LOG_CRIT, "%s: Failed to find server information for [%s]", appname, inet_ntoa(server)); return 1; } /* init server vars (timeout, retries, secret, backup) */ actdelay = actsrvptr->resp_timeout; actretry = actsrvptr->retries; acttmos = 0; memcpy(actsecret, actsrvptr->shared_secret, 16); if (!bflag && !actbackup) { actbackaddr.s_addr = actsrvptr->backup_address.s_addr; actbackup = 0; } if (debug) { printf("%s: ServerInfo: Timeout=%i, Retries=%i, BackupSrvr=[%s]\n", appname, actdelay, actretry, inet_ntoa(actbackaddr)); printf("%s: Secret= ", appname); display_mem(actsecret, sizeof(actsecret)); } /* do udp connect */ rc = api_connect(actfd, &actrsa, IPPROTO_UDP, appname, 1); if (rc == 0) { /* set socket non-blocking for receives */ /* ioctl(actfd, FIONBIO, (char *)&on); negated by new recv loop*/ #ifdef ACCT_START if (!bflag) /* possible recursion - not reentrable */ rc = radius_send_acct_stat(actfd, PW_ACCT_ON, nasaddr, 0); #endif } } return rc; } /* radius_close_acct() - Close socket to RADIUS Server * * Called for subsystem re-initialization * Sends Accounting-Off Status message * Frees dynamically allocated memory * Closes socket * * Inputs: actfd socket * Outputs: actfd socket is closed * Returns: void */ void radius_close_acct(nasaddr) UINT32 nasaddr; { #ifdef _WIN32 SESREC *wpnt, *npnt; #endif /* _WIN32 */ #ifdef ACCT_START radius_send_acct_stat(actfd, PW_ACCT_OFF, nasaddr, PW_CAUSE_ADMIN_RESET); #endif #ifdef _WIN32 /* free dynamic memory for session blocks */ wpnt = seslist; while (wpnt != NULL) { npnt = wpnt->next; free(wpnt); wpnt = npnt; } #endif /* _WIN32 */ api_close(actfd); /* close socket */ return; } /* radius_close_actserver() - Close logical session to RADIUS Server * * Called for program shutdown * Sends Accounting-Off Status message * only if we were ever initialized and connected * * Inputs: actfd socket * Outputs: Accounting-Off sent, remote socket address cleared * Returns: void */ void radius_close_actserver(nasaddr, cuz) UINT32 nasaddr; int cuz; { if ((actinit == 1) && (actrsa.sin_addr.s_addr != 0)) { #ifdef ACCT_START radius_send_acct_stat(actfd, PW_ACCT_OFF, nasaddr, cuz); #endif actrsa.sin_addr.s_addr = 0; } return; } /* radius_fix_act_auth() - Do the authenticator for an Accounting request message * * Calculate and insert authenticator for message * Assumes message is otherwise complete; id and length have been set * * Inputs: startp = start of message * endp = end of message * newauth = pointer to buffer to store new authenticator * Outputs: Authenticator is written into buffer */ void radius_fix_act_auth(startp, endp, newauth) u_char *startp, *endp, *newauth; { int slen; bzero(startp+4, 16); /* zero auth slot */ for(slen=0; (slen<16) && (actsecret[slen] != '\0'); slen++); /* variable length secret */ memcpy(endp, actsecret, slen); /* append secret to outgoing message */ MDString(startp, (endp-startp)+slen, newauth); /* run through MD5 routine */ memcpy(startp+4, newauth, 16); /* put digest in authenticator slot */ bzero(endp, slen); /* zero secret copy in msg buffer */ return; } #ifdef _WIN32 /* radius_find_acntg_session - Lookup Accounting session context * * Inputs: Annex IP address, Port, Username * cflag = 1: Start session: overwrite stale or create new * cflag = 0: End session: return found w/no changes, or create new * cflag = -1: Non session: return if found, but don't create new * * The session id assigned is derived from the current logid upper two bytes; * and an incrementing counter in the lower two bytes * * Returns: pointer to context block * or NULL if allocation failure or cflag = -1 and not found */ SESREC *radius_find_acntg_session(ipaddr, port, user, logid, service, cflag) UINT32 ipaddr; SECPORT *port; u_char *user; UINT32 logid; int service, cflag; { SESREC *sespnt = seslist; u_char nlstr[1]; nlstr[0] = 0; if (user == NULL) user = nlstr; /* avoid problems with strcmp and strcpy */ /* look for matching record on addr & port */ while (sespnt != NULL) { if ((sespnt->nasport.unit == port->unit) && (sespnt->nasport.type == port->type) && (sespnt->nasaddr == ipaddr) ) break; sespnt = sespnt->next; } /* not found at all, create a new one or return NULL */ if (sespnt == NULL) { if (cflag == -1) return sespnt; /* allocate memory block */ if ((sespnt = (SESREC *)malloc(sizeof(SESREC))) != NULL) { /* init contents */ sespnt->sesid = (logid & 0xFFFF0000) | (++nextses & 0x0000FFFF); sespnt->nasaddr = ipaddr; sespnt->nasport.type = port->type; sespnt->nasport.unit = port->unit; strcpy(sespnt->username, user); sespnt->starttime = ((cflag) ? time(NULL) : (time_t)0); sespnt->srvaddr.s_addr = 0; sespnt->ataddr.s_addr = 0; sespnt->iservice = service; sespnt->actses = 1; sespnt->totses = 1; sespnt->next = seslist; /* link to front */ seslist = sespnt; if (debug && (cflag == 0)) printf("%s: UNMATCHED Logout!\n", appname); if (debug) printf("%s: Sesrec created block=%p User=%s, NASaddr=[%s], Port=%s, Sesid=%08lX\n", appname, sespnt, user, inet_ntoa(*(struct in_addr *)&ipaddr), port_to_name(port, portname), sespnt->sesid); } else if (debug) printf("%s: Sesrec create failed: User=%s, NASaddr=[%s], Port=%s\n", appname, user, inet_ntoa(*(struct in_addr *)&ipaddr), port_to_name(port,portname)); } /* check if this record matches on username */ else if (strcmp(sespnt->username, user) == 0) { if (cflag != 1) return sespnt; /* if lookup or logout, just return */ /* look for nesting this login */ if ((sespnt->iservice == SERVICE_CLI_HOOK)) { sespnt->actses++; sespnt->totses++; } else { /* write over prior record */ sespnt->sesid = (logid & 0xFFFF0000) | (++nextses & 0x0000FFFF); sespnt->starttime = time(NULL); sespnt->srvaddr.s_addr = 0; sespnt->ataddr.s_addr = 0; sespnt->iservice = service; sespnt->actses = 1; sespnt->totses = 1; if (debug) printf("%s: Sesrec update block=%p User=%s, NASaddr=[%s], Port=%s, Sesid=%08lX\n", appname, sespnt, user, inet_ntoa(*(struct in_addr *)&ipaddr), port_to_name(port, portname), sespnt->sesid); } } else { if (cflag == -1) return NULL; /* write over old record */ { strcpy(sespnt->username, user); sespnt->sesid = (logid & 0xFFFF0000) | (++nextses & 0x0000FFFF); sespnt->starttime = time(NULL); sespnt->srvaddr.s_addr = 0; sespnt->iservice = service; sespnt->actses = 1; sespnt->totses = 1; if (debug) printf("%s: Sesrec overwrite block=%p User=%s, NASaddr=[%s], Port=%s, Sesid=%08lX\n", appname, sespnt, user, inet_ntoa(*(struct in_addr *)&ipaddr), port_to_name(port,portname), sespnt->sesid); } } return sespnt; } /* radius_free_session() - Deallocate Accounting session context * * Inputs: pointer to context block */ void radius_free_session(sptr) SESREC *sptr; { SESREC *wptr = seslist; if (sptr == NULL) return; if (seslist == sptr) { seslist = sptr->next; } else while (wptr != NULL) { if (wptr->next == sptr) { wptr->next = sptr->next; break; } wptr = wptr->next; } /* freed - found or not */ if (debug) printf ("%s: Sesrec free block=%p, Sesid=%08lX\n", appname, sptr, sptr->sesid); free((u_char *)sptr); return; } #endif /* defined _WIN32 */ #ifdef ACCT_START /* radius_send_acct_stat() - Send RADIUS Accounting status message * * This routine builds & sends Accounting Start and Stop messages * * Inputs: fd = socket number * type = status type code * Returns: 0 = sent * -1 = not sent * Side effects: uses startp, bufp */ int radius_send_acct_stat(fd, type, nasaddr, cause) int fd, cause; UINT32 type, nasaddr; { #ifndef _WIN32 struct hostent *hentp; #endif /* defined _WIN32 */ /* allocate buffer */ if ((startp = bufp = (u_char *)malloc(MAX_RAD_MSG)) != NULL) { radius_build_header(&bufp, PW_ACCOUNTING_REQUEST, ++actseq, NULL); RADIUS_BUILD_INT(&bufp, PW_ACCT_STATUS_TYPE, type); if (nasaddr != 0) { RADIUS_BUILD_INT(&bufp, PW_NAS_IP_ADDRESS, nasaddr); /* for this NAS */ #ifndef _WIN32 if ((hentp = gethostbyaddr((char *)&nasaddr, sizeof(nasaddr), AF_INET)) != NULL) { radius_add_attribute(&bufp, PW_NAS_IDENTIFIER, strlen(hentp->h_name), hentp->h_name, 0); } #endif /* defined _WIN32 */ } if (cause != 0) RADIUS_BUILD_INT(&bufp, PW_ACCT_TERMINATE_CAUSE, cause); radius_fix_length(startp, bufp); /* put in length */ radius_fix_act_auth(startp, bufp, reqauth); /* build authenticator */ delayp = NULL; radius_send_log(startp, (bufp-startp), NULL); /* send msg and wait for ack */ free(startp); /* dealloc send buffer */ } else { syslog(LOG_ERR, "%s: malloc failure sending status msg", appname); return -1; } return 0; } #endif /* radius_send_log() - Send RADIUS Accounting message * * Sends message and waits for ack. * If timeout, try again until counter expire, * Then try backup server * * Global: actfd - accounting socket, connected to server * Inputs: smsg - pointer to message body to send * Returns: 0 = success, <0 error */ int radius_send_log(smsg, slen, sesptr) u_char *smsg; int slen; #ifdef _WIN32 SESREC *sesptr; #else /* not defined _WIN32 */ SESPROFILE *sesptr; #endif /* not defined _WIN32 */ { int rc = 0; int rlen = MAX_RAD_MSG; u_char *rcvp, *tptr; u_long ntime; int dcnt; int code_val = 0; /* setup receive buffer, don't send if cannot receive */ if ((rcvp = (u_char *)malloc(rlen))) { /* try current first, if failure then backup */ do { /* Do this until count or break */ for (acttmos=0; acttmos 0) { code_val = (int)*smsg; syslog(LOG_DEBUG, "Sent RADIUS %s to %s", (code_val < NCODETYPES && code_val > 0)? codetype[code_val - 1]: "\0", inet_ntoa(actrsa.sin_addr)); if (debug) printf("%s: message #%i sent okay\n", appname, actseq); /* read messages until we get the sequence id we want or timeout/error or discard max reached */ dcnt = 0; do { rc = api_recvwait(actfd, rcvp, rlen, actdelay, 0, 1, appname); dcnt++; } while ((rc > 0) && (rcvp[1] != actseq) && (dcnt < DISCARD_COUNT)); if (rc <= 0) { syslog(LOG_DEBUG, "No response from RADIUS server %s", inet_ntoa(actrsa.sin_addr)); /* timeout or error */ if (debug) { printf("%s: request timeout/err: error code=%i, retry count=%i, #%i, server=[%s]\n", appname, rc, acttmos, actseq, inet_ntoa(actrsa.sin_addr)); } /* if timeout, modify message to attempt to send again */ if ((rc == -2) && (delayp != NULL)) { /* increment sequence id and delay time attribute */ smsg[1] = ++actseq; /* NOTE: this will change the message and authenticator, but not the length */ tptr = delayp; /* retrieve pointer to delay time, but don't let it get changed */ bcopy((char *)(tptr+2), (char *)&ntime, 4); RADIUS_BUILD_INT(&tptr, PW_ACCT_DELAY_TIME, (UINT32)TIMEDIFF(time(NULL), sndtime)); radius_fix_act_auth(smsg, smsg+slen, reqauth); } } else { if (debug) { printf("%s: response recvd buf=%p len=%i id=%u\n", appname, rcvp, rc, rcvp[1]); /* display_mem(rcvp, rc); */ } /* check if response message is authentic and valid */ #ifdef _WIN32 if ((rc = radius_parse_server_response(rcvp, rc, actseq, reqauth, actrsa.sin_addr, NULL)) == PW_ACCOUNTING_RESPONSE) { #else /* not defined _WIN32 */ if ((rc = radius_parse_server_response(rcvp, rc, actseq, reqauth, actrsa.sin_addr)) == PW_ACCOUNTING_RESPONSE) { #endif /* not defined _WIN32 */ code_val = (int)*rcvp; #ifdef USE_SYSLOG syslog(LOG_DEBUG, "Received RADIUS %s from %s", (code_val < NCODETYPES && code_val > 0)? codetype[code_val - 1]: "\0", inet_ntoa(actrsa.sin_addr)); #endif if (debug) printf("%s: ack on #%i received okay\n", appname, actseq); rc = 0; break; } /* else bad response; try again next time around the loop*/ if (debug) printf("%s: response failed authentication; code=%i on #%i\n", appname, rc, actseq); } } else { /* send failure */ if (debug) printf("%s: send failure rc=%i errno=%i\n", appname, rc, errno); syslog(LOG_ERR, "%s: send datagram failed ", appname); } } /* if current server failed, failover or quit */ if (acttmos >= actretry) { syslog(LOG_ERR, "%s: Transmit retries exceeded for server [%s]", appname, inet_ntoa(actrsa.sin_addr)); if (debug) printf( "%s: Transmit retries exceeded for server [%s]\n", appname, inet_ntoa(actrsa.sin_addr)); /* if not the backup server, open the backup */ if (!actbackup) { if (actbackaddr.s_addr != 0) { actwasaddr.s_addr = actrsa.sin_addr.s_addr; actbackup = 1; /* try to open backup server */ if (radius_open_acct(actbackaddr, (UINT32)0, 1) == 0) { if (sesptr) sesptr->srvaddr.s_addr = actrsa.sin_addr.s_addr; /* remember to prevent future switch */ radius_fix_act_auth(smsg, smsg+slen, reqauth); /* redo authenticator on new secret */ continue; } syslog(LOG_ERR, "%s: Failed to open Backup server", appname); } else { /* no backup server defined */ syslog(LOG_CRIT, "%s: No Backup server defined for failover", appname); } } /* backup server has failed too! log event */ else { if (debug) printf("%s: Backup server retries exceeded for [%s]\n", appname, inet_ntoa(actrsa.sin_addr)); syslog(LOG_ERR, "%s: Backup server retries exceeded for [%s]", appname, inet_ntoa(actrsa.sin_addr)); } #ifdef USE_SYSLOG syslog(LOG_CRIT, "%s: RADIUS Accounting shutting down", appname); #endif actfailed = 1; /* set flag to indicate logging disabled */ rc = -1; break; } else { /* sent okay */ break; } } while (actfailed == 0); /*deallocate recv buffer */ free(rcvp); } else { /* allocation error */ syslog(LOG_ERR, "%s: malloc failure for response recv bfr", appname); return -1; } return rc; } /* radius_build_log() - Build and send RADIUS accounting message * * General call to log information to a RADIUS accounting server * * Called from write_audit_log() in acp_lib.c * * Inputs: arguments of info to log * event = type of log * nasaddr = IP address of NAS * port = NAS Port descriptor * user = username * service = service type * usraddr = user's address * stats = statistics block * msg = message text * * Side effects: global variables are used for the message build and server status * A session context block is started for Login type sessions * * Outputs: accounting request message is sent to RADIUS server * * Returns: 0= if log message generated * 1= if non-logged event on RADIUS * 2= User not in RADIUS regime * -or- * radius not enabled (win32 only) * -1= failure */ #ifndef _WIN32 int radius_build_log(event,logid,nasaddr,port,user,service,usraddr,stats,msg ) int event, service; UINT32 logid, nasaddr; SECPORT *port; char *user, *msg; NetAddr *usraddr; LOG_PORT_STATS *stats; { int rlogtype = PW_STATUS_START; /* default */ struct environment_spec envblk; struct profileses sesblk; char sesidbuf[9]; int cflag, rc=0; UINT32 tval,stype; struct attrib_handle ahndl; struct radius_attribute atrblk; struct hostent *hentp; struct in_addr negoaddr; if (!raddb_up) return (-1); /* db must be up */ if (actinit == 0) { if ((rc = radius_init_acnt()) != 0 ) return rc; } if (debug) { printf("Acplog: Event=%s, Service=%s, NASip=[%s], Port=%s, User=%s, Msg=%s\n", event_name[event], service_name[service], inet_ntoa(*(struct in_addr *)&nasaddr), port_to_name(port, portname), nullchk(user), nullchk(msg)); if (stats) printf("%s:\tStats: In: %li, %li\tOut: %li, %li\n", appname, stats->pkts_rx, stats->bytes_rx, stats->pkts_tx, stats->bytes_tx); } /* Check type of log message event * Only interested the following events: LOGIN, LOGOUT, ACCT * Others ignored by RADIUS for now * * Defined in: acp_policy.h Strings in: acp_lib.c */ switch (event) { case EVENT_LOGIN: cflag = 1; /* create or update block for this session */ rlogtype = PW_STATUS_START; /* session start msg */ break; case EVENT_NEGO_ADDR: cflag = -1; rlogtype = PW_IPCP_START; break; case EVENT_LOGOUT: case EVENT_ACCT: cflag = 0; /* use existing block, or create temp */ rlogtype = PW_STATUS_STOP; /* end of session message and free context */ break; case EVENT_BOOT: /* Annex just rebooted */ ses_nas_reboot(nasaddr); /* clear sessions on this NAS */ return 2; /* leave it in acp_logfile */ default: /* ignore all other events */ if (debug>1) printf("%s: not a loggable event type\n", appname); return 2; } /* Check service type, and reject if not a RADIUS supported Annex service type */ stype = xa2_service_type[service]; if (stype == -1) return 2; if (port->type == DEV_MP) return 2; /* ignore events on multi-link port */ if ((user == NULL) || (strlen(user) == 0)) return 2; /* if no user, Consider not RADIUS */ /* Now, we like this event; * fill in environment block with the information * Find or Create a session context */ bzero((char *)&envblk, sizeof(envblk)); envblk.annex = nasaddr; envblk.port = port->unit; envblk.ptype = port->type; envblk.protocol = service; envblk.group_list = (struct group_entry *)NULL; get_time_stamp(&envblk.time); /* use system local time, ignore failures */ strncpy(envblk.username, user, sizeof(envblk.username)-1); /* find our session context, if not: then not radius user */ switch (cflag) { case -1: if ((rc = ses_lookup(&envblk, &sesblk, SDB_FINDACTIVEUSER)) <= 0) { if (debug) printf("%s: Lookup search status %i for %s\n", appname, rc, user); return 2; } if (debug > 1) printf("%s: Processing log\n", appname); break; case 1: if ((rc = ses_login(&envblk, &sesblk, logid)) != 0) { if (debug) printf("%s: Login search status %i for %s\n", appname, rc, user); return rc; } if (debug > 1) printf("%s: Processing login\n", appname); break; case 0: if ((rc = ses_logout(&envblk, &sesblk, event, logid)) != 0) { if (debug) printf("%s: Logout search status %i for %s\n", appname, rc, user); return rc; } if (debug > 1) printf("%s: Processing logout\n", appname); break; default: return 2; /* shouldn't happen but sesptr would be invalid */ } /* If server unassigned for some underlying reason, we cannot send anything */ if ((sesblk.srvaddr.s_addr == -1L) || (sesblk.srvaddr.s_addr == 0)) { if (rlogtype == PW_STATUS_STOP) ses_delete(&envblk, &sesblk); if (debug) printf("%s: Not a RADIUS regime (srvaddr=%s)\n", appname, inet_ntoa(sesblk.srvaddr)); return 2; } /* test if we have shut down accounting, if so don't touch the network */ if (actfailed != 0) { /* if all done, free-up session record */ if (rlogtype == PW_STATUS_STOP) ses_delete(&envblk, &sesblk); return (-1); } /* Make socket point to our server */ /* NOTE!: if we are on the backup server, this value will be the primary server * and not agree with the current socketaddr contents. * Use the sockaddr value for all real server address needs */ rc = radius_open_acct(sesblk.srvaddr, nasaddr, 0); if (rc != 0) { if (rc == 1) actfailed = 1; /* no recovery if we cannot get it up at all */ return -1; /* just fail up */ } /* for Nego Address, parse the address out of the msg string * and insert into local session context */ if (event == EVENT_NEGO_ADDR) { if (strncmp(msg, "ip ", 3) == 0) { if ((negoaddr.s_addr = inet_addr(msg+3)) != -1) { if (sesblk.aservice == 0) sesblk.aservice = PW_FRAMED_USER; sesblk.aprotocol = PW_PPP; sesblk.ataddr = negoaddr; msg = NULL; ses_update(&envblk, &sesblk, SDB_FINDACTIVEUSER, EVENT_NEGO_ADDR); } } } /* Build and send the message to the current server*/ /* if we can allocate a buffer */ if ((startp = bufp = (u_char *)malloc(MAX_RAD_MSG)) != NULL) { if (debug) printf("%s: Building accounting message\n", appname); /* build accounting message */ radius_build_header(&bufp, PW_ACCOUNTING_REQUEST, ++actseq, NULL); RADIUS_BUILD_INT(&bufp, PW_ACCT_STATUS_TYPE, rlogtype); /* session info from saved data */ RADIUS_BUILD_STR(&bufp, PW_USER_NAME, sesblk.username); RADIUS_BUILD_INT(&bufp, PW_NAS_IP_ADDRESS, nasaddr); if ((hentp = gethostbyaddr((char *)&nasaddr, sizeof(nasaddr), AF_INET)) != NULL) { radius_add_attribute(&bufp, PW_NAS_IDENTIFIER, strlen(hentp->h_name), hentp->h_name, 0); } /* for non-serial port types that cannot be encoded into a RADIUS port type, * put port type into high order part of port number */ tval = radius_convert_type(port->type); if ((tval != PW_PORT_VIRTUAL) && (tval != -1)) { RADIUS_BUILD_INT(&bufp, PW_NAS_PORT, port->unit); } else { RADIUS_BUILD_INT(&bufp, PW_NAS_PORT, ((port->type * VPORT_MULT) + port->unit)); } if (tval != -1) RADIUS_BUILD_INT(&bufp, PW_NAS_PORT_TYPE, tval); /* do calling information */ if (strlen(sesblk.caller) > 0 ) RADIUS_BUILD_STR(&bufp, PW_CALLING_STATION_ID, sesblk.caller); if (strlen(sesblk.called) > 0 ) RADIUS_BUILD_STR(&bufp, PW_CALLED_STATION_ID, sesblk.called); if (sesblk.aservice) { RADIUS_BUILD_INT(&bufp, PW_USER_SERVICE_TYPE, (u_long)sesblk.aservice); } else if (stype != -1) { RADIUS_BUILD_INT(&bufp, PW_USER_SERVICE_TYPE, stype); } /* service attributes based on authorization */ /* NOTE: not all the following services are currently supported, * but the case is simply paired with the nearest equivalent */ switch (sesblk.aservice) { case PW_FRAMED_USER: case PW_DIALBACK_FRAMED_USER: if (!sesblk.aprotocol) goto report_protocol; RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, (u_long)sesblk.aprotocol); switch (sesblk.aprotocol) { case PW_PPP: case PW_SLIP: if (!sesblk.ataddr.s_addr) goto report_address; RADIUS_BUILD_INT(&bufp, PW_FRAMED_ADDRESS, sesblk.ataddr.s_addr); break; case PW_ARAP: case PW_IPXSLIP: goto report_address; break; } break; case PW_LOGIN_USER: case PW_DIALBACK_LOGIN_USER: if (sesblk.aprotocol == 255) goto report_protocol; RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, (u_long)sesblk.aprotocol); if ((sesblk.aprotocol == PW_TELNET) && (sesblk.aport)) RADIUS_BUILD_INT(&bufp, PW_LOGIN_TCP_PORT, (u_long)sesblk.aport); if (sesblk.ataddr.s_addr) RADIUS_BUILD_INT(&bufp, PW_LOGIN_HOST, sesblk.ataddr.s_addr); break; case PW_NAS_PROMPT_USER: case PW_ADMINISTRATIVE_USER: case PW_CALLBACK_PROMPT_USER: break; case PW_OUTBOUND_USER: if (sesblk.aport) RADIUS_BUILD_INT(&bufp, PW_LOGIN_TCP_PORT, (u_long)sesblk.aport); break; case PW_AUTHENTICATE_USER: default: /* fall through */ report_protocol: /* service attributes based on delivery */ switch (service) { case SERVICE_RLOGIN: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_RLOGIN); break; case SERVICE_TELNET: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_TELNET); break; case SERVICE_CONNECT: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_LAT); break; case SERVICE_SLIP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_SLIP); break; case SERVICE_PPP: case SERVICE_SYNC_PPP: case SERVICE_VPN_PPP: case SERVICE_MP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_PPP); break; case SERVICE_ARAP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_ARAP); break; case SERVICE_IPX: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_IPXSLIP); break; } report_address: /* remote or host address in event record */ if (usraddr) { switch (usraddr->type) { case IP_ADDRT: /* if login service then this is the Host address */ if (stype == PW_LOGIN_USER) { RADIUS_BUILD_INT(&bufp, PW_LOGIN_HOST, usraddr->n.ip_addr.inet); } else { RADIUS_BUILD_INT(&bufp, PW_FRAMED_ADDRESS, usraddr->n.ip_addr.inet); } break; case IPX_ADDRT: RADIUS_BUILD_INT(&bufp, PW_FRAMED_IPXNET, usraddr->n.ipx_addr.network); break; case LAT_ADDRT: RADIUS_BUILD_STR(&bufp, PW_LOGIN_LAT_SERVICE, usraddr->n.lat_addr.service); RADIUS_BUILD_STR(&bufp, PW_LOGIN_LAT_NODE, usraddr->n.lat_addr.node); break; } } } /* end switch (aservice) */ /* accounting specific fields */ #ifndef USE_64 sprintf(sesidbuf, "%08.8lX", sesblk.sesid); #else sprintf(sesidbuf, "%08.8X", sesblk.sesid); #endif RADIUS_BUILD_STR(&bufp, PW_ACCT_SESSION_ID, sesidbuf); RADIUS_BUILD_INT(&bufp, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS ); if (sesblk.class[0]) { radius_add_attribute(&bufp, PW_CLASS, sesblk.class[0], sesblk.class + 1, 0); } delayp = bufp; /* save for later retry update */ RADIUS_BUILD_INT(&bufp, PW_ACCT_DELAY_TIME, 0); sndtime = time(NULL); if (event != EVENT_LOGIN) /* calc elapsed session time */ if (sesblk.starttime) /* but don't send if start is zero*/ RADIUS_BUILD_INT(&bufp, PW_ACCT_SESSION_TIME, (UINT32)TIMEDIFF(time(NULL), sesblk.starttime) ); if (stats) { RADIUS_BUILD_INT(&bufp, PW_ACCT_INPUT_PACKETS, stats->pkts_rx); RADIUS_BUILD_INT(&bufp, PW_ACCT_OUTPUT_PACKETS, stats->pkts_tx); RADIUS_BUILD_INT(&bufp, PW_ACCT_INPUT_OCTETS, stats->bytes_rx); RADIUS_BUILD_INT(&bufp, PW_ACCT_OUTPUT_OCTETS, stats->bytes_tx); } /* RADIUS_BUILD_INT(&bufp, PW_ACCT_TERMINATE_CAUSE, ?? ); * reason code */ if (msg) RADIUS_BUILD_STR(&bufp, PW_PORT_MESSAGE, msg); /* to see whats here */ radius_fix_length(startp, bufp); /* put in length */ radius_fix_act_auth(startp, bufp, reqauth); /* build authenticator */ if (debug) { printf("%s: Built Accounting message #%u\n", appname, actseq); display_mem(startp, (bufp-startp)); } rc = radius_send_log(startp, (bufp-startp), &sesblk); /* send msg and wait for ack */ free(startp); /* dealloc send buffer */ } else { /* allocation error */ syslog(LOG_ERR, "%s: malloc failure in send log, send bfr", appname); return -1; } /* if all done, free-up session record */ if (rlogtype == PW_STATUS_STOP) { ses_delete(&envblk, &sesblk); } return rc; } #else /* defined _WIN32 */ int radius_build_log(event,logid,nasaddr,port,user,service,usraddr,stats,msg ) int event, service; UINT32 logid, nasaddr; SECPORT *port; char *user, *msg; NetAddr *usraddr; LOG_PORT_STATS *stats; { int rlogtype = PW_STATUS_START; /* default */ SESREC *sesptr; char sesidbuf[9]; int cflag, rc=0; UINT32 tval,stype; struct in_addr negoaddr; if ( !ErpcdOpt->RadiusAuthentication ) { return 2; /* Radius not enabled, try other logging */ } if ( !ErpcdOpt->UseRadiusLogging ) { return 2; /* Not a Radius logged event */ } if (actfailed != 0) return (-1); if (actinit == 0) { if ((rc = radius_init_acnt()) != 0 ) return rc; } if (debug) { printf("%s: Event=%s, Service=%s, NASip=[%s], Port=%s, User=%s, Msg=%s\n", appname, event_name[event], service_name[service], inet_ntoa(*(struct in_addr *)&nasaddr), port_to_name(port, portname), nullchk(user), nullchk(msg)); if (stats) printf("%s:\tStats: In: %li, %li\tOut: %li, %li\n", appname, stats->pkts_rx, stats->bytes_rx, stats->pkts_tx, stats->bytes_tx); } /* Check type of log message event * Only interested the following events: LOGIN, LOGOUT, ACCT * Others ignored by RADIUS for now * * Defined in: acp_policy.h Strings in: acp_lib.c */ switch (event) { case EVENT_LOGIN: cflag = 1; /* create or update block for this session */ rlogtype = PW_STATUS_START; /* session start msg */ break; case EVENT_NEGO_ADDR: cflag = -1; rlogtype = PW_IPCP_START; break; case EVENT_LOGOUT: case EVENT_ACCT: cflag = 0; /* use existing block, or create temp */ rlogtype = PW_STATUS_STOP; /* end of session message and free context */ break; default: /* ignore all other events */ if (debug) printf("%s: not a loggable event type\n", appname); return 2; } /* Check service type, and reject if not a RADIUS supported Annex service type */ stype = xa2_service_type[service]; if (stype == -1) return 2; if (port->type == DEV_MP) return 2; /* Ignore MP requests */ if ((user == NULL) || (strlen(user) == 0)) return 2; /* if no user, Consider not RADIUS */ if (stype == PW_LOGIN_USER) cflag = -1; /* if terminal service, do not update session block */ /* Now, Find or Create a session context */ if ((sesptr = radius_find_acntg_session(nasaddr, port, user, logid, service, cflag)) == NULL) { if (cflag != -1) { /* allocation error */ syslog(LOG_ERR, "%s: malloc failure creating session block", appname); return -1; } else { return 2; /* not radius */ } } /* If we don't have a server address yet (Login), figure out where this should go * taking into account that we may have failed over to the backup server */ if (sesptr->srvaddr.s_addr == 0) { /* Get the accounting server, the address has been resolved and saved as the default accounting server */ sesptr->srvaddr = default_servers->acct_server; } /* If not in RADIUS regime or otherwise unassigned, Bail out now */ if ((sesptr->srvaddr.s_addr == -1L) || (sesptr->srvaddr.s_addr == 0)) { if (rlogtype == PW_STATUS_STOP) radius_free_session(sesptr); if (debug) printf("%s: Not a RADIUS regime (srvaddr=%08lX)\n", appname, sesptr->srvaddr.s_addr); return 2; } /* If Login type service, this is nested inside of CLI session for start/stop purposes */ /* NOTE: This could be fixed by some sort of nested or multisession logging */ if (stype == PW_LOGIN_USER) return 1; /* For RADIUS session stops, In the following cases we use the ACCT event instead of LOGOUT: * not if service_from == SERVICE_PORTS * yes if port_from_type == DEV_VIRTUAL * or service_from == {TELNET, RLOGIN, CONNECT} * Indicate to standard logging that this is an ignored but RADIUS event */ if (event == EVENT_LOGOUT) { if (service == SERVICE_PORTS) return 1; if (!((port->type == DEV_VIRTUAL) || ((service == SERVICE_TELNET) || (service == SERVICE_RLOGIN) || (service == SERVICE_CONNECT)))) return 1; } /* if a nested login/logout, ignore this event */ if (sesptr->actses > 1) { if (rlogtype == PW_STATUS_STOP) sesptr->actses--; return 1; } /* Make socket point to our server */ /* NOTE!: if we are on the backup server, this value will be the primary server * and not agree with the current socketaddr contents. * Use the sockaddr value for all real server address needs */ rc = radius_open_acct(sesptr->srvaddr, nasaddr, 0); if (rc != 0) { if (rc == 1) actfailed = 1; /* no recovery if we cannot get it up at all */ return -1; /* just fail up */ } /* Build and send the message to the current server*/ /* if we can allocate a buffer */ if ((startp = bufp = (u_char *)malloc(MAX_RAD_MSG)) != NULL) { if (debug) printf("%s: Building accounting message\n", appname); /* build accounting message */ radius_build_header(&bufp, PW_ACCOUNTING_REQUEST, ++actseq, NULL); RADIUS_BUILD_INT(&bufp, PW_ACCT_STATUS_TYPE, rlogtype); /* session info from saved data */ RADIUS_BUILD_STR(&bufp, PW_USER_NAME, sesptr->username); RADIUS_BUILD_INT(&bufp, PW_NAS_IP_ADDRESS, sesptr->nasaddr); /* for non-serial port types that cannot be encoded into a RADIUS port type, * put port type into high order part of port number */ tval = radius_convert_type(port->type); if ((tval != PW_PORT_VIRTUAL) && (tval != -1)) { RADIUS_BUILD_INT(&bufp, PW_NAS_PORT, port->unit); } else { RADIUS_BUILD_INT(&bufp, PW_NAS_PORT, ((port->type * VPORT_MULT) + port->unit)); } if (tval != -1) RADIUS_BUILD_INT(&bufp, PW_NAS_PORT_TYPE, tval); if (stype != -1) RADIUS_BUILD_INT(&bufp, PW_USER_SERVICE_TYPE, stype); /* service attributes */ switch (service) { case SERVICE_RLOGIN: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_RLOGIN); break; case SERVICE_TELNET: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_TELNET); break; case SERVICE_CONNECT: RADIUS_BUILD_INT(&bufp, PW_LOGIN_SERVICE, PW_LAT); break; case SERVICE_SLIP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_SLIP); break; case SERVICE_PPP: case SERVICE_SYNC_PPP: case SERVICE_VPN_PPP: case SERVICE_MP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_PPP); break; case SERVICE_ARAP: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_ARAP); break; case SERVICE_IPX: RADIUS_BUILD_INT(&bufp, PW_FRAMED_PROTOCOL, PW_IPXSLIP); break; } /* for Nego Address, parse the address out of the msg string * and insert into local session context */ if (event == EVENT_NEGO_ADDR) { if (strncmp(msg, "ip ", 3) == 0) { if ((negoaddr.s_addr = inet_addr(msg+3)) != -1) { RADIUS_BUILD_INT(&bufp, PW_FRAMED_ADDRESS, negoaddr.s_addr); msg = NULL; sesptr->ataddr = negoaddr; } } } /* remote or host address */ else if (usraddr) { switch (usraddr->type) { case IP_ADDRT: /* if login service then this is the Host address */ if (stype == PW_LOGIN_USER) { RADIUS_BUILD_INT(&bufp, PW_LOGIN_HOST, usraddr->n.ip_addr.inet); } else { RADIUS_BUILD_INT(&bufp, PW_FRAMED_ADDRESS, usraddr->n.ip_addr.inet); } break; case IPX_ADDRT: RADIUS_BUILD_INT(&bufp, PW_FRAMED_IPXNET, usraddr->n.ipx_addr.network); break; case LAT_ADDRT: RADIUS_BUILD_STR(&bufp, PW_LOGIN_LAT_SERVICE, usraddr->n.lat_addr.service); RADIUS_BUILD_STR(&bufp, PW_LOGIN_LAT_NODE, usraddr->n.lat_addr.node); break; } } /* do we have a saved nego address? */ else if (sesptr->ataddr.s_addr) { switch (stype) { case PW_FRAMED_USER: RADIUS_BUILD_INT(&bufp, PW_FRAMED_ADDRESS, sesptr->ataddr.s_addr); break; } } /* accounting specific fields */ sprintf(sesidbuf, "%08lX", sesptr->sesid); RADIUS_BUILD_STR(&bufp, PW_ACCT_SESSION_ID, sesidbuf); RADIUS_BUILD_INT(&bufp, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS ); delayp = bufp; /* save for later retry update */ RADIUS_BUILD_INT(&bufp, PW_ACCT_DELAY_TIME, 0); sndtime = time(NULL); if (event != EVENT_LOGIN) /* calc elapsed session time */ if (sesptr->starttime) /* but don't send if start is zero*/ RADIUS_BUILD_INT(&bufp, PW_ACCT_SESSION_TIME, (UINT32)TIMEDIFF(time(NULL), sesptr->starttime) ); if (stats) { RADIUS_BUILD_INT(&bufp, PW_ACCT_INPUT_PACKETS, stats->pkts_rx); RADIUS_BUILD_INT(&bufp, PW_ACCT_OUTPUT_PACKETS, stats->pkts_tx); RADIUS_BUILD_INT(&bufp, PW_ACCT_INPUT_OCTETS, stats->bytes_rx); RADIUS_BUILD_INT(&bufp, PW_ACCT_OUTPUT_OCTETS, stats->bytes_tx); } /* RADIUS_BUILD_INT(&bufp, PW_ACCT_TERMINATE_CAUSE, ?? ); * reason code */ if (msg) RADIUS_BUILD_STR(&bufp, PW_PORT_MESSAGE, msg); /* to see whats here */ radius_fix_length(startp, bufp); /* put in length */ radius_fix_act_auth(startp, bufp, reqauth); /* build authenticator */ if (debug) { printf("%s: Built Accounting message #%u\n", appname, actseq); display_mem(startp, (bufp-startp)); } rc = radius_send_log(startp, (bufp-startp), sesptr); /* send msg and wait for ack */ free(startp); /* dealloc send buffer */ } else { /* allocation error */ syslog(LOG_ERR, "%s: malloc failure in send log, send bfr", appname); return -1; } /* if all done, free-up session record */ if (rlogtype == PW_STATUS_STOP) { radius_free_session(sesptr); } return rc; } #endif /* defined _WIN32 */