/* * * RADIUS * Remote Authentication Dial In User Service * * * Livingston Enterprises, Inc. * 4464 Willow Road * Pleasanton, CA 94588 * * Copyright 1992-1996 Livingston Enterprises, Inc. All Rights Reserved. * * This software is provided under license from Livingston * Enterprises, Inc., the terms and conditions of which are set * forth in a Software License Agreement that is contained in an * End User Agreement contained in the product packaging, and * electronically on the Livingston ftp site. This software may * only be used in conjunction with Livingston (or Livingston * authorized) products. Livingston makes no warranties to any * licensee concerning the applicability of the software to * licensee's specific requirements or the suitability of the * software for any intended use. Licensee shall not remove, * modify or alter any copyright and/or other proprietary rights * notice and must faithfully reproduce all such notices on any * copies or modifications to this software that it makes. * * Livingston Enterprises, Inc. makes no representations about the * suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. * */ /* don't look here for the version, run radiusd -v or look in version.c */ static char sccsid[] = "$Id: radiusd.c,v 1.43 1997/06/05 08:20:48 cdr Exp $ Copyright 1992 Livingston Enterprises Inc"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "radius.h" #include "users.h" #define SIGCHLD 20 #define WNOHANG 1 AUTH_REQ *radrecv (UINT4,u_short,u_char *,int); char recv_buffer[4096]; char send_buffer[4096]; char *progname; int sockfd; int acctfd; int debug_flag = 0; int max_requests; int max_request_time; char *radius_dir; char *radacct_dir; u_short radius_port; UINT4 expiration_seconds; UINT4 warning_seconds; extern int errno; static AUTH_REQ *first_request; char unknown[8]; struct sockaddr_in rad_saremote; void sig_fatal(); void sig_hup(); void sig_cleanup(); void process_menu(); char *ipaddr2strp(); void md5_calc(); int main (int argc,char ** argv) { int result; struct sockaddr_in salocal; struct sockaddr_in saremote; struct sockaddr_in *sin; struct servent *svp; u_short lport; char argval; int t; fd_set readfds; int status; int atoi(); int config_init(); int dict_init(); void log_version(); void rad_exit(); void rad_request(); void version(); int on = 1; progname = *argv++; argc--; if ( getenv("RADIUS_DEBUG") ) debug_flag = 1; max_requests = MAX_REQUESTS; max_request_time = MAX_REQUEST_TIME; radacct_dir = RADACCT_DIR; radius_dir = RADIUS_DIR; signal(SIGHUP, sig_hup); signal(SIGINT, sig_fatal); signal(SIGQUIT, sig_fatal); signal(SIGILL, sig_fatal); signal(SIGTRAP, sig_fatal); signal(SIGIOT, sig_fatal); signal(SIGFPE, sig_fatal); signal(SIGTERM, sig_fatal); signal(SIGCHLD, sig_cleanup); /* Initialize the dictionary */ if( dict_init() ) rad_exit(SS$_INSFARG); /* Initialize Configuration Values */ if ( config_init() ) rad_exit(SS$_INSFARG); /* * Open the user table */ if( !(1 & (status = user_open())) ) rad_exit(status); /* Show our stuff */ log_version(); if ( svp = getservbyname ("radius", "udp") ) lport = (u_short) svp->s_port; else lport = htons(PW_AUTH_UDP_PORT); DEBUG("using udp port %d for RADIUS\n", ntohs(lport)); if ( 0 > (sockfd = socket (AF_INET, SOCK_DGRAM, 0)) ) { log_err("auth socket error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0 ) { log_err("auth socket error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } sin = (struct sockaddr_in *) & salocal; memset ((char *) sin, '\0', sizeof (salocal)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = lport; if ( 0 > bind (sockfd, (struct sockaddr *)&salocal, sizeof (*sin)) ) { log_err("auth bind error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } /* * Open Accounting Socket. */ if ( svp = getservbyname ("radacct", "udp") ) lport = (u_short) svp->s_port; else lport = htons(PW_ACCT_UDP_PORT); DEBUG("using udp port %d for RADIUS accounting\n",ntohs(lport)); if ( 0 > (acctfd = socket (AF_INET, SOCK_DGRAM, 0)) ) { log_err("acct socket error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } if (0 > setsockopt(acctfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ) { log_err("acct socket error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } sin = (struct sockaddr_in *) & salocal; memset ((char *) sin, '\0', sizeof (salocal)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = lport; if ( 0 > bind (acctfd, (struct sockaddr *)&salocal, sizeof (*sin)) ) { log_err("acct bind error %s\n", sys_errlist[errno]); rad_exit(SS$_ABORT); } /* * Receive user requests */ sin = (struct sockaddr_in *) & saremote; for(;;) { FD_ZERO(&readfds); if ( sockfd >= 0 ) FD_SET(sockfd, &readfds); if ( acctfd >= 0 ) FD_SET(acctfd, &readfds); DEBUG("waiting for request by select () ...\n"); status = select(32, &readfds, NULL, NULL, NULL); if ( status == -1 ) { if (errno == EINTR) continue; sig_fatal(101); } if ( sockfd >= 0 && FD_ISSET(sockfd, &readfds) ) rad_request(sockfd); if ( acctfd >=0 && FD_ISSET(acctfd, &readfds) ) rad_request(acctfd); } } /************************************************************************* * * Function: rad_request * * Purpose: Receive UDP client requests * *************************************************************************/ void rad_request ( int fd ) { int salen; int result; struct sockaddr_in *sin; AUTH_REQ *authreq; void radrespond(); salen = sizeof (rad_saremote); sin = (struct sockaddr_in *) & rad_saremote; result = recvfrom (fd, (char *) recv_buffer, (int) sizeof(recv_buffer), (int) 0, (struct sockaddr *)&rad_saremote, & salen); if (result < AUTH_HDR_LEN) { log_err("rad_request: runt packet of %d bytes\n",result); return; } authreq = radrecv( ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port), recv_buffer, result); radrespond(authreq, fd); } /************************************************************************* * * Function: radrecv * * Purpose: Receive UDP client requests, build an authorization request * structure, and attach attribute-value pairs contained in * the request to the new structure. * *************************************************************************/ AUTH_REQ *radrecv ( UINT4 host, u_short udp_port, u_char *buffer, int length ) { u_char *ptr; AUTH_HDR *auth; int totallen; int attribute; int attrlen; DICT_ATTR *attr; DICT_ATTR *dict_attrget(); UINT4 lvalue; VALUE_PAIR *first_pair; VALUE_PAIR *prev; VALUE_PAIR *pair; AUTH_REQ *authreq; void rad_exit(); if (length < AUTH_HDR_LEN) { /* too short to be real */ return ((AUTH_REQ *)NULL); } /* * Pre-allocate the new request data structure */ if((authreq = (AUTH_REQ *)malloc(sizeof(AUTH_REQ))) == (AUTH_REQ *)NULL) { log_err("radrecv: fatal system error: out of memory, exiting\n"); rad_exit(SS$_INSFMEM); } auth = (AUTH_HDR *)buffer; totallen = ntohs(auth->length); if (totallen > length) { /* truncated packet, ignore */ log_err("radrecv: request from client %s claimed length %d, only %d bytes found\n", ipaddr2strp((u_long)host), totallen, length); free(authreq); return((AUTH_REQ *)NULL); } DEBUG("radrecv: Request from host %s code=%d, id=%d, length=%d\n", ipaddr2strp((u_long)host), auth->code, auth->id, totallen); /* * Fill header fields */ authreq->ipaddr = host; authreq->udp_port = udp_port; authreq->id = auth->id; authreq->code = auth->code; memcpy(authreq->vector, auth->vector, AUTH_VECTOR_LEN); /* * Extract attribute-value pairs */ ptr = auth->data; length = totallen - AUTH_HDR_LEN; first_pair = (VALUE_PAIR *)NULL; prev = (VALUE_PAIR *)NULL; while(length > 0) { attribute = *ptr++; attrlen = *ptr++; if(attrlen < 2) { length = 0; continue; } attrlen -= 2; if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) { DEBUG("received unknown attribute %d\n", attribute); } else if ( attrlen >= AUTH_STRING_LEN ) { DEBUG("attribute %d too long, length of %d >= %d\n", attribute, attrlen, AUTH_STRING_LEN); } else { if((pair = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == (VALUE_PAIR *)NULL) { log_err("radrecv: fatal system error: out of memory, exiting\n"); rad_exit(SS$_INSFMEM); } strcpy(pair->name, attr->name); pair->attribute = attr->value; pair->type = attr->type; pair->next = (VALUE_PAIR *)NULL; switch(attr->type) { case PW_TYPE_STRING: memcpy(pair->strvalue, ptr, attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; debug_pair(pair); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: memcpy(&lvalue, ptr, sizeof(UINT4)); pair->lvalue = ntohl(lvalue); debug_pair(pair); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break; default: DEBUG(" %s (Unknown Type %d)\n", attr->name,attr->type); free(pair); break; } } ptr += attrlen; length -= attrlen + 2; } authreq->request = first_pair; return(authreq); } /************************************************************************* * * Function: radrespond * * Purpose: Respond to supported requests: * * PW_AUTHENTICATION_REQUEST - Authentication request from * a client network access server. * * PW_ACCOUNTING_REQUEST - Accounting request from * a client network access server. * * PW_PASSWORD_REQUEST - User request to change a password. * *************************************************************************/ void radrespond ( AUTH_REQ *authreq, int activefd ) { char *ip_hostname(); int rad_authenticate(); void rad_accounting(); if (authreq == (AUTH_REQ *)NULL) { return; } switch(authreq->code) { case PW_AUTHENTICATION_REQUEST: rad_authenticate(authreq, activefd); break; case PW_ACCOUNTING_REQUEST: rad_accounting(authreq, activefd); break; /*@RRL ??? case PW_PASSWORD_REQUEST: rad_passchange(authreq, activefd); break; */ default: log_err("unknown request type %d from %s ignored\n", authreq->code, ip_hostname(authreq->ipaddr)); reqfree(authreq); break; } } void sig_cleanup ( int sig ) { signal(SIGCHLD, sig_cleanup); } /************************************************************************* * * Function: rad_authenticate * * Purpose: Process and reply to an authentication request * *************************************************************************/ int rad_authenticate ( AUTH_REQ *authreq, int activefd ) { VALUE_PAIR *attr,*auth_item,*check_item,*get_attribute(), *namepair,*password_item,*user_check,*user_reply; char auth_name[AUTH_STRING_LEN + 2]; char hold_vector[AUTH_VECTOR_LEN]; char pw_digest[16]; char string[AUTH_STRING_LEN + 2]; char umsg[AUTH_STRING_LEN + 2]; char *client_hostname(); char *encpw; char *ip_hostname(); char *ptr; char *user_msg; int authtype; int i; int j; int passlen; int result; int retval; int speed = 0,session_limit = 1 ,rewind_flag,conn_type = 0; int calc_digest(); void calc_next_digest(); void send_accept(); void send_reject(); /* Get the username from the request */ namepair = get_attribute(authreq->request, PW_USER_NAME); if( (!namepair) || (!strlen(namepair->strvalue)) ) { log_err("auth: access-request from %s ignored; no user name\n", ip_hostname(authreq->ipaddr)); reqfree(authreq); return(0); } /* Verify the client and calculate the MD5 Password Digest */ if ( calc_digest(pw_digest, authreq) ) { /* We do not respond when this fails */ log_err("auth: access-request from unknown client %s ignored; user name %s\n", ip_hostname(authreq->ipaddr), namepair->strvalue); reqfree(authreq); return(0); } /* * If the request is processing a menu, service it here. */ if ( (attr = get_attribute(authreq->request, PW_STATE)) && (!strncmp(attr->strvalue, "MENU=", 5)) ) { process_menu(authreq, activefd, pw_digest); return(0); } for (rewind_flag = 0;;rewind_flag++) { /* Get the user from the database */ if ( result = user_find( namepair->strvalue, auth_name, &user_check, &user_reply,rewind_flag) ) { log_err("auth: access-request from %s (%s) denied for unknown user %s\n", client_hostname(authreq->ipaddr), ipaddr2strp(authreq->ipaddr), namepair->strvalue); send_reject(authreq, (char *)NULL, activefd); reqfree(authreq); return(0); } /* Validate the user */ /* Look for matching check items */ password_item = (VALUE_PAIR *)NULL; authtype = PW_AUTHTYPE_LOCAL; user_msg = (char *)NULL; check_item = user_check; while ( !result && check_item ) { auth_item = get_attribute(authreq->request, check_item->attribute); switch(check_item->attribute) { case PW_PREFIX: case PW_SUFFIX: break; case PW_EXPIRATION: break; case PW_PASSWORD: if ( !strcmp(check_item->strvalue, "VMS") || !strcmp(check_item->strvalue, "UNIX") ) authtype = PW_AUTHTYPE_VMS; else password_item = check_item; break; case PW_AUTHTYPE: authtype = check_item->lvalue; break; case PW_CRYPT_PASSWORD: authtype = PW_AUTHTYPE_CRYPT; password_item = check_item; break; case PW_SESSION_LIMIT: session_limit = check_item->lvalue; DEBUG("auth: session limit %d\n",session_limit); break; case PW_CONNECT_RATE: auth_item = get_attribute(authreq->request, PW_CONNECT_INFO); if ( auth_item ) { speed = atoi(auth_item->strvalue); DEBUG("auth: connection rate is %d\n",speed); if ( speed > check_item->lvalue ) { log_err("auth: connection rate with %d is not allowed\n",speed); result = -1; } } break; default: if(auth_item == (VALUE_PAIR *)NULL) { result = -1; break; } switch(check_item->type) { case PW_TYPE_STRING: if(strcmp(check_item->strvalue, auth_item->strvalue) != 0) { result = -1; } break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: if(check_item->lvalue != auth_item->lvalue) result = -1; break; default: result = -1; break; } break; } check_item = check_item->next; } if (result != -1) { break; } } /* * At this point we have validated all normal comparisons * for the user. All that is left is the actual authentication. * Authentication will be done based on the authentication type * previously specified. */ if(result == 0) { /* * Decrypt the password in the request. */ if((auth_item = get_attribute(authreq->request, PW_PASSWORD)) != (VALUE_PAIR *)NULL) { passlen = auth_item->lvalue; if(passlen > AUTH_MAXPASS_LEN) { passlen = AUTH_MAXPASS_LEN; } memcpy(string, auth_item->strvalue, AUTH_MAXPASS_LEN); ptr = string; for(i = 0;i < passlen;i += AUTH_PASS_LEN) { /* * Store the vector to be used in next segment * of the encrypted password. */ memcpy(hold_vector, ptr, AUTH_VECTOR_LEN); /* Decrypt from the digest */ for(j = 0;j < AUTH_PASS_LEN;j++) { *ptr ^= pw_digest[j]; ptr++; } /* Calculate the next digest if necessary */ if(i < passlen) { calc_next_digest(pw_digest, authreq->secret, hold_vector); } } *ptr = '\0'; } else { string[0] = '\0'; } switch(authtype) { case PW_AUTHTYPE_VMS: DEBUG("auth: VMS/authentication\n"); if ( auth_item ) result = vms_login(auth_name, string); #ifdef __RADIUS_ENHANCED__ /* ** Check connection rate allowed by SYSUAF */ if ( !getenv("RADIUS_DISABLE_RIGHTSCHECK") ) { auth_item = get_attribute(authreq->request,PW_CONNECT_INFO); if ( !result && auth_item && (speed = atoi(auth_item->strvalue)) ) { DEBUG("auth: VMS/Check connection rate allowed\n"); DEBUG("auth: connection rate is %d\n",speed); if ( speed > 33600 && speed <= 56*1024 ) { if ( result = vms_right(auth_name, "56K") ) log_err("Connection speed: %d;\n\r56K access not authorized - connection denied for %s.", speed,auth_name); } } auth_item = get_attribute(authreq->request, PW_CLIENT_PORT_TYPE); if ( !result && auth_item && (auth_item->lvalue > 1) ) { speed = auth_item->lvalue; DEBUG("auth: VMS/Check connection type allowed\n"); DEBUG("auth: connection type is %d\n",speed); if ( result = vms_right(auth_name, "ISDN") ) log_err("Connection type: %d;\n\rISDN access not authorized - connection denied for user %s.", speed,auth_name); } if ( !result && auth_item && !vms_right(auth_name,"DUALPORT") ) session_limit = 2; if ( !result && auth_item && !getenv("RADIUS_DISABLE_SESSIONLIMIT") ) { char nas_and_port [32]; short port = 0; if ( auth_item = get_attribute(authreq->request,PW_CLIENT_PORT_ID) ) port = auth_item->lvalue; sprintf(nas_and_port,"%15s:%-3d",ipaddr2strp(authreq->ipaddr),port); DEBUG("auth: session limit check for %s@(%s)\n",auth_name,nas_and_port); if ( result = vms_get_stat(auth_name,nas_and_port,session_limit,&speed)) log_err("auth:Session limit (%d) exceeded;\n\rConnection #%d denied for user %s@%s.", session_limit,speed+1,auth_name,ip_hostname(authreq->ipaddr)); } } #endif __RADIUS_ENHANCED__ break; case PW_AUTHTYPE_LOCAL: default: /* * The local authentication type supports normal * password comparison and the Three-Way CHAP. */ if( !password_item ) { if(string[0] != '\0') result = -1; } else if ( auth_item ) { if ( strcmp(password_item->strvalue,string) ) result = -1; } else if((auth_item = get_attribute(authreq->request, PW_CHAP_PASSWORD)) != (VALUE_PAIR *)NULL) { /* Use MD5 to verify */ ptr = string; *ptr++ = *auth_item->strvalue; strcpy(ptr, password_item->strvalue); ptr += strlen(password_item->strvalue); memcpy(ptr, authreq->vector, AUTH_VECTOR_LEN); md5_calc(pw_digest, string, 1 + CHAP_VALUE_LENGTH + strlen(password_item->strvalue)); /* Compare them */ if(memcmp(pw_digest, auth_item->strvalue + 1, CHAP_VALUE_LENGTH) != 0) { result = -1; } } else { result = -1; } break; case PW_AUTHTYPE_CRYPT: case PW_AUTHTYPE_REJECT: result = -1; break; } } if( result ) send_reject(authreq, user_msg, activefd); else send_accept(authreq, user_reply, user_msg, activefd); reqfree(authreq); pairfree(user_check); pairfree(user_reply); return(0); } /************************************************************************* * * Function: send_reject * * Purpose: Reply to the request with a REJECT. Also attach * any user message provided. * *************************************************************************/ void send_reject ( AUTH_REQ *authreq, char *msg, int activefd ) { AUTH_HDR *auth; struct sockaddr_in saremote; struct sockaddr_in *sin; char *client_hostname(); char digest[AUTH_VECTOR_LEN]; int secretlen; int total_length; u_char *ptr; int len; int block_len; auth = (AUTH_HDR *)send_buffer; /* Build standard response header */ if(authreq->code == PW_PASSWORD_REQUEST) { auth->code = PW_PASSWORD_REJECT; } else { auth->code = PW_AUTHENTICATION_REJECT; } auth->id = authreq->id; memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); total_length = AUTH_HDR_LEN; ptr = auth->data; /* Append the user message */ if(msg != (char *)NULL) { len = strlen(msg); do { if(len > AUTH_STRING_LEN) { block_len = AUTH_STRING_LEN; } else { block_len = len; } *ptr++ = PW_PORT_MESSAGE; *ptr++ = block_len + 2; memcpy(ptr, msg, block_len); msg += block_len; ptr += block_len; total_length += block_len + 2; len -= block_len; } while(len > 0); } /* Set total length in the header */ auth->length = htons(total_length); /* Calculate the response digest */ secretlen = strlen((const char *)authreq->secret); memcpy(send_buffer + total_length, authreq->secret, secretlen); md5_calc(digest, (char *)auth, total_length + secretlen); memcpy(auth->vector, digest, AUTH_VECTOR_LEN); memset(send_buffer + total_length, 0, secretlen); sin = (struct sockaddr_in *) &saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(authreq->ipaddr); sin->sin_port = htons(authreq->udp_port); DEBUG("Sending Reject of id %d to %s (%s)\n", authreq->id, client_hostname(authreq->ipaddr), ipaddr2strp(authreq->ipaddr)); /* Send it to the user */ if ( 0 > sendto(activefd, (char *)auth, (int)total_length, (int)0, (struct sockaddr *) &saremote, sizeof(struct sockaddr_in)) ) { log_err("Sending Reject of id %d to %s (%s)\n,%s", authreq->id, client_hostname(authreq->ipaddr), ipaddr2strp(authreq->ipaddr),sys_errlist[errno]); } } /************************************************************************* * * Function: send_challenge * * Purpose: Reply to the request with a CHALLENGE. Also attach * any user message provided and a state value. * *************************************************************************/ void send_challenge(authreq, msg, state, activefd) AUTH_REQ *authreq; char *msg; char *state; int activefd; { AUTH_HDR *auth; struct sockaddr_in saremote; struct sockaddr_in *sin; char *client_hostname(); char digest[AUTH_VECTOR_LEN]; int secretlen; int total_length; u_char *ptr; int len; int block_len; auth = (AUTH_HDR *)send_buffer; /* Build standard response header */ auth->code = PW_ACCESS_CHALLENGE; auth->id = authreq->id; memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); total_length = AUTH_HDR_LEN; ptr = auth->data; /* Append the user message */ if(msg != (char *)NULL && (len = strlen(msg)) > 0) { while(len > 0) { if(len > AUTH_STRING_LEN) { block_len = AUTH_STRING_LEN; } else { block_len = len; } *ptr++ = PW_PORT_MESSAGE; *ptr++ = block_len + 2; memcpy(ptr, msg, block_len); msg += block_len; ptr += block_len; total_length += block_len + 2; len -= block_len; } } /* Append the state info */ if((state != (char *)NULL) && (strlen(state) > (size_t)0)) { len = strlen(state); *ptr++ = PW_STATE; *ptr++ = len + 2; memcpy(ptr, state, len); ptr += len; total_length += len + 2; } /* Set total length in the header */ auth->length = htons(total_length); /* Calculate the response digest */ secretlen = strlen((const char *)authreq->secret); memcpy(send_buffer + total_length, authreq->secret, secretlen); md5_calc(digest, (char *)auth, total_length + secretlen); memcpy(auth->vector, digest, AUTH_VECTOR_LEN); memset(send_buffer + total_length, 0, secretlen); sin = (struct sockaddr_in *) &saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(authreq->ipaddr); sin->sin_port = htons(authreq->udp_port); DEBUG("Sending Challenge of id %d to %s (%s)\n", authreq->id, client_hostname(authreq->ipaddr), ipaddr2strp(authreq->ipaddr)); /* Send it to the user */ sendto(activefd, (char *)auth, (int)total_length, (int)0, (struct sockaddr *) &saremote, sizeof(struct sockaddr_in)); } /************************************************************************* * * Function: send_pwack * * Purpose: Reply to the request with an ACKNOWLEDGE. * User password has been successfully changed. * *************************************************************************/ void send_pwack(authreq, activefd) AUTH_REQ *authreq; int activefd; { AUTH_HDR *auth; struct sockaddr_in saremote; struct sockaddr_in *sin; char *ip_hostname(); char digest[AUTH_VECTOR_LEN]; int secretlen; auth = (AUTH_HDR *)send_buffer; /* Build standard response header */ auth->code = PW_PASSWORD_ACK; auth->id = authreq->id; memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); auth->length = htons(AUTH_HDR_LEN); /* Calculate the response digest */ secretlen = strlen((const char *)authreq->secret); memcpy(send_buffer + AUTH_HDR_LEN, authreq->secret, secretlen); md5_calc(digest, (char *)auth, AUTH_HDR_LEN + secretlen); memcpy(auth->vector, digest, AUTH_VECTOR_LEN); memset(send_buffer + AUTH_HDR_LEN, 0, secretlen); sin = (struct sockaddr_in *) &saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(authreq->ipaddr); sin->sin_port = htons(authreq->udp_port); DEBUG("Sending PW Ack of id %d to %lx (%s)\n", authreq->id, (u_long)authreq->ipaddr, ip_hostname(authreq->ipaddr)); /* Send it to the user */ sendto(activefd, (char *)auth, (int)AUTH_HDR_LEN, (int)0, (struct sockaddr *)&saremote, sizeof(struct sockaddr_in)); } /************************************************************************* * * Function: send_accept * * Purpose: Reply to the request with an ACKNOWLEDGE. Also attach * reply attribute value pairs and any user message provided. * *************************************************************************/ void send_accept(authreq, reply, msg, activefd) AUTH_REQ *authreq; VALUE_PAIR *reply; char *msg; int activefd; { AUTH_HDR *auth; u_short total_length; struct sockaddr_in saremote; struct sockaddr_in *sin; u_char *ptr; int len; UINT4 lvalue; u_char digest[16]; int secretlen; char *client_hostname(); VALUE_PAIR *menu_attr; VALUE_PAIR *get_attribute(); char *get_menu(); int block_len; char state_value[100]; /* Check to see if the response is a menu */ if((menu_attr = get_attribute(reply, PW_MENU)) != (VALUE_PAIR *)NULL) { msg = get_menu(menu_attr->strvalue); sprintf(state_value, "MENU=%s", menu_attr->strvalue); send_challenge(authreq, msg, state_value, activefd); return; } auth = (AUTH_HDR *)send_buffer; /* Build standard header */ auth->code = PW_AUTHENTICATION_ACK; auth->id = authreq->id; memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); total_length = AUTH_HDR_LEN; DEBUG("Sending Accept of id %d to %s (%s)\n", authreq->id, client_hostname(authreq->ipaddr), ipaddr2strp(authreq->ipaddr)); /* Load up the configuration values for the user */ ptr = auth->data; while(reply != (VALUE_PAIR *)NULL) { debug_pair(reply); *ptr++ = reply->attribute; switch(reply->type) { case PW_TYPE_STRING: len = strlen(reply->strvalue); if (len >= AUTH_STRING_LEN) { len = AUTH_STRING_LEN - 1; } *ptr++ = len + 2; memcpy(ptr, reply->strvalue,len); ptr += len; total_length += len + 2; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: *ptr++ = sizeof(UINT4) + 2; lvalue = htonl(reply->lvalue); memcpy(ptr, &lvalue, sizeof(UINT4)); ptr += sizeof(UINT4); total_length += sizeof(UINT4) + 2; break; default: break; } reply = reply->next; } /* Append the user message */ if(msg != (char *)NULL && (len = strlen(msg)) > 0) { while(len > 0) { if(len > AUTH_STRING_LEN) { block_len = AUTH_STRING_LEN; } else { block_len = len; } *ptr++ = PW_PORT_MESSAGE; *ptr++ = block_len + 2; memcpy(ptr, msg, block_len); msg += block_len; ptr += block_len; total_length += block_len + 2; len -= block_len; } } auth->length = htons(total_length); /* Append secret and calculate the response digest */ secretlen = strlen((const char *)authreq->secret); memcpy(send_buffer + total_length, authreq->secret, secretlen); md5_calc(digest, (char *)auth, total_length + secretlen); memcpy(auth->vector, digest, AUTH_VECTOR_LEN); memset(send_buffer + total_length, 0, secretlen); sin = (struct sockaddr_in *) &saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(authreq->ipaddr); sin->sin_port = htons(authreq->udp_port); /* Send it to the user */ if ( 0 > sendto(activefd, (char *)auth, (int)total_length, (int)0, (struct sockaddr *)&saremote, sizeof(struct sockaddr_in)) ) log_err("Sending Accept\n,%s",sys_errlist[errno]); } /************************************************************************* * * Function: calc_digest * * Purpose: Validates the requesting client NAS. Calculates the * digest to be used for decrypting the users password * based on the clients private key. * *************************************************************************/ int calc_digest ( char *digest, AUTH_REQ *authreq ) { char buffer[128]; char secret[64]; char hostnm[256]; char * ip_hostname(); int secretlen; FILE * clientfd; long ipaddr; if( !(clientfd = fopen(RADIUS_CLIENTS, "r")) ) { fprintf(stderr, "%s: couldn't open RADIUS_CLIENTS\n", progname); return(-1); } ipaddr = (UINT4)0; while( fgets(buffer, sizeof(buffer), clientfd) ) { if(*buffer == '#') continue; if(sscanf(buffer, "%s%s", hostnm, secret) != 2) continue; ipaddr = get_ipaddr(hostnm); if(ipaddr == authreq->ipaddr) break; } fclose(clientfd); /* * Validate the requesting IP address - * Not secure, but worth the check for accidental requests */ if(ipaddr != authreq->ipaddr) { strcpy(hostnm,ip_hostname(ipaddr)); log_err("requester address mismatch: %s != %s\n", hostnm,ip_hostname(authreq->ipaddr)); memset(secret, 0, sizeof(secret)); return(-1); } /* Use the secret to setup the decryption digest */ memset(buffer, 0, sizeof(buffer)); secretlen = strlen((const char *)secret); strcpy((char *)buffer, (const char *)secret); memcpy(buffer + secretlen, authreq->vector, AUTH_VECTOR_LEN); md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN); strcpy((char *)authreq->secret, (const char *)secret); memset(buffer, 0, sizeof(buffer)); memset(secret, 0, secretlen); return(0); } /************************************************************************* * * Function: calc_next_digest * * Purpose: Calculates the digest to be used for decrypting the * users password past the first 16 octets based on the clients * private key. * *************************************************************************/ void calc_next_digest(digest, secret, vector) u_char *digest; u_char *secret; u_char *vector; { u_char buffer[128]; int secretlen; /* Use the secret to setup the decryption digest */ memset(buffer, 0, sizeof(buffer)); secretlen = strlen((const char *)secret); strcpy((char *)buffer, (const char *)secret); memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN); md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN); memset(buffer, 0, sizeof(buffer)); } /************************************************************************* * * Function: client_hostname * * Purpose: Return the cached client name if we have one. Otherwise * use the regular ip_hostname() function. * *************************************************************************/ char * client_hostname ( long ipaddr ) { return(ip_hostname(ipaddr)); } /************************************************************************* * * Function: debug_pair * * Purpose: Print the Attribute-value pair to the desired File. * *************************************************************************/ void debug_pair(pair) VALUE_PAIR * pair; { void fprint_attr_val(); if(debug_flag) { fprint_attr_val(-1, pair); } } /************************************************************************* * * Function: config_init * * Purpose: intializes configuration values: * * expiration_seconds - When updating a user password, * the amount of time to add to the current time * to set the time when the password will expire. * This is stored as the VALUE Password-Expiration * in the dictionary as number of days. * * warning_seconds - When acknowledging a user authentication * time remaining for valid password to notify user * of password expiration. * *************************************************************************/ int config_init (void) { DICT_VALUE *dval; DICT_VALUE *dict_valfind(); if((dval = dict_valfind("Password-Expiration")) == (DICT_VALUE *)NULL) { expiration_seconds = (UINT4)0; } else { expiration_seconds = dval->value * (UINT4)SECONDS_PER_DAY; } if((dval = dict_valfind("Password-Warning")) == (DICT_VALUE *)NULL) { warning_seconds = (UINT4)0; } else { warning_seconds = dval->value * (UINT4)SECONDS_PER_DAY; } return(0); } /************************************************************************* * * Function: get_attribute * * Purpose: Retrieve a specific value-pair from a list of value-pairs. * *************************************************************************/ VALUE_PAIR * get_attribute(value_list, attribute) VALUE_PAIR *value_list; int attribute; { while(value_list != (VALUE_PAIR *)NULL) { if(value_list->attribute == attribute) { return(value_list); } value_list = value_list->next; } return((VALUE_PAIR *)NULL); } /************************************************************************* * * Function: reqfree * * Purpose: Release the memory used by an AUTH_REQ structure * *************************************************************************/ void reqfree ( AUTH_REQ *authreq ) { if ( authreq ) { pairfree(authreq->request); memset(authreq, 0, sizeof(AUTH_REQ)); free(authreq); } } void sig_fatal ( int sig ) { log_err("exit on signal %d\n", sig); rad_exit(SS$_ABORT); } void sig_hup ( int sig ) { return; } void rad_exit ( int rc ) { sys$exit(rc); }