/*********************************************************************** RADIUS Remote Authentication Dial In User Service Lucent Technologies Remote Access 4464 Willow Road Pleasanton, CA 94588 Copyright 1992-1999 Lucent Technologies Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Lucent Technologies and its contributors. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided by the copyright holders and contributors ``as is'' and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holder or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. ************************************************************************/ /*********************************************************************** RADIUS client test program RADIUS 2.1 includes an example client program called radtest, that sends a RADIUS packet to a server running on the same host as radtest, and prints out the attributes returned. It doesn't support accounting packets yet. It always fills in the NAS-IP-Address as 127.0.0.1 and the NAS-Port as 1. Passwords longer than 16 characters are not supported. It looks for its dictionary in the same directory its run from. radtest -v prints the version. radtest -h prints help: ./radtest -d called_id -f -g calling_id -h -i id -p port -s secret -t type -v -x -u username password The other flags work as follows: -a (not implemented yet) -d Called Station Id -f Send framed dialin hint -g Calling Station Id -i Use id as the packet identifier -n (not implemented yet) -p Use port as the port (defaults to definition in /etc/services, or 1645) -r (not implemented yet) -s to specify shared secret (defaults to "localkey") -t send type as service type (overrides -f) -u Specifies username and password (notice that this takes two arguments) -x sets debugging level (not used for anything yet) ************************************************************************/ static char sccsid[] = "$Id: radtest.c,v 1.1 1999/06/23 23:40:43 cdr Exp $ Copyright 1992-1999 Lucent Technologies Inc"; #include "stdio.h" #include "stdlib.h" #include "errno.h" #include "sys/types.h" #include "sys/socket.h" #include "sys/param.h" #include "sys/time.h" #include "netdb.h" #include "netinet/in.h" #include "radius.h" int fd; char * host_name; struct sockaddr_in addr; struct hostent * hostent; char * secret; int secret_len; char * progname; u_char req_id; char r_buf[4096]; char s_buf[4096]; char u_name[256]; char u_passwd[256]; int u_name_len; int u_passwd_len; int debug_flag = 0; int debug_mem = 0; char *radius_dir = "."; char *radius_log = "/dev/tty"; UINT4 now = 0; main(argc, argv) char * argv[]; { AUTH_HDR *ah; AUTH_REQ *authreq; AUTH_REQ *radrecv(); VALUE_PAIR *pair; char *called_id; char *calling_id; char argnum; char argval; fd_set fdset; int acct_flag; int count; int frame_flag; int host; int i; int len; int rc; int sa_len; int service_type; int val; int verbose_flag; struct sockaddr_in sa_ihd; struct timeval tv; struct servent * svp; u_char *cp; u_char alen; u_char id; u_short radius_port; void rad_exit(); void usage(); void version(); u_short lport; char passwd[AUTH_PASS_LEN]; char md5_buf[1024]; acct_flag = 0; count = 1; frame_flag = 0; id = 0; service_type = 0; /* default is not to send service type */ verbose_flag = 0; secret = "localkey"; host_name = "localhost"; radius_port = 0; radius_dir = "."; /* always looks in current dir for dictionary */ called_id = NULL; calling_id = NULL; progname = *argv++; argc--; while(argc > 0) { if(**argv != '-') { usage(); } argval = *(*argv + 1); argnum = *(*argv + 2); argc--; argv++; switch(argval) { case 'a': /* accounting packet */ acct_flag = 1; break; case 'd': /* calleD station id */ if(argc == 0) { usage(); } called_id = *argv; argc--; argv++; break; case 'f': frame_flag = 1; break; case 'g': /* callinG station id */ if(argc == 0) { usage(); } calling_id = *argv; argc--; argv++; break; case 'h': usage(); break; case 'i': /* id to use */ if(argc == 0) { usage(); } id = (u_char)(strtol(*argv, 0, 10) & 0x000000ff); argc--; argv++; break; case 'n': /* count not implemented yet */ usage(); if(argc == 0) { usage(); } count = (u_short)strtol(*argv, 0, 10); argc--; argv++; break; case 'p': /* set radius port */ if(argc == 0) { usage(); } radius_port = (u_short)atoi(*argv); argc--; argv++; break; case 'r': /* RADIUS server to send to */ if(argc == 0) { usage(); } host_name = *argv; argc--; argv++; break; case 's': /* shared secret */ if(argc == 0) { usage(); } secret = *argv; argc--; argv++; break; case 't': /* set service type */ if(argc == 0) { usage(); } service_type = (u_short)atoi(*argv); argc--; argv++; break; case 'u': /* username and password */ if(argc < 2) { usage(); } strcpy(u_name,*argv); argv++; strcpy(u_passwd,*argv); argv++; argc -= 2; break; case 'v': version(); break; case 'x': if (isdigit(argnum)) { verbose_flag = argnum - '0'; } else { verbose_flag++; } break; default: usage(); break; } } /* Initialize the dictionary */ if(dict_init() != 0) { rad_exit(-1); } secret_len = strlen(secret); if (radius_port) { lport = htons(radius_port); } else { if (acct_flag) { svp = getservbyname ("radacct", "udp"); if (svp != (struct servent *) 0) { lport = (u_short) svp->s_port; } else { lport = htons(ntohs(PW_AUTH_UDP_PORT)); } } else { svp = getservbyname ("radius", "udp"); if (svp != (struct servent *) 0) { lport = (u_short) svp->s_port; } else { lport = htons(ntohs(PW_ACCT_UDP_PORT)); } } } if ((hostent = gethostbyname(host_name)) == (struct hostent *)NULL) { fprintf(stderr, "gethostbyname<%s> error<%d>\n", host_name, errno); exit(1); } memcpy( &addr.sin_addr.s_addr, hostent->h_addr, hostent->h_length); host = ntohl(addr.sin_addr.s_addr); addr.sin_family = AF_INET; addr.sin_port = lport; printf("Radius client: server is host: %s %s Port: %d\n", host_name, inet_ntoa(addr.sin_addr), ntohs(lport)); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(errno); } /* * build packet and send it */ ah = (AUTH_HDR *)s_buf; memset(ah, 0, sizeof(*ah)); if (acct_flag) { ah->code = PW_ACCOUNTING_REQUEST; } else { ah->code = PW_AUTHENTICATION_REQUEST; } ah->id = id; len = AUTH_HDR_LEN; u_name_len = strlen(u_name); u_passwd_len = strlen(u_passwd); /* * user name is mandatory */ cp = ah->data; *cp++ = PW_USER_NAME; *cp++ = u_name_len + 2; memcpy(cp, u_name, u_name_len); cp += u_name_len; len += u_name_len + 2; /* * password doesn't handle longer than 16 characters, for now */ if (passwd && service_type != PW_CALL_CHECK_USER ) { *cp++ = PW_PASSWORD; *cp++ = AUTH_PASS_LEN + 2; if (u_passwd_len > AUTH_PASS_LEN) { u_passwd_len = AUTH_PASS_LEN; } memset(passwd, 0, AUTH_PASS_LEN); memcpy(passwd, u_passwd, u_passwd_len); strcpy(md5_buf, secret); memcpy(md5_buf + secret_len, ah->vector, AUTH_VECTOR_LEN); md5_calc(cp, md5_buf, secret_len + AUTH_VECTOR_LEN); for (i = 0; i < AUTH_PASS_LEN; i++) { *cp++ ^= passwd[i]; } len += AUTH_PASS_LEN + 2; } /* * client id */ alen = 4; *cp++ = PW_CLIENT_ID; *cp++ = alen + 2; val = htonl(0x7f000001); /* 127.0.0.1 */ memcpy(cp, &val, alen); cp += alen; len += alen + 2; /* * client port id */ alen = 4; *cp++ = PW_CLIENT_PORT_ID; *cp++ = alen + 2; val = htonl(1); memcpy(cp, &val, alen); cp += alen; len += alen + 2; /* * framed hint */ if (service_type) { alen = 4; *cp++ = PW_USER_SERVICE_TYPE; *cp++ = alen + 2; val = htonl(service_type); memcpy(cp, &val, alen); cp += alen; len += alen + 2; } else if (frame_flag) { alen = 4; *cp++ = PW_USER_SERVICE_TYPE; *cp++ = alen + 2; val = htonl(PW_FRAMED_USER); memcpy(cp, &val, alen); cp += alen; len += alen + 2; alen = 4; *cp++ = PW_FRAMED_PROTOCOL; *cp++ = alen + 2; val = htonl(PW_PPP); memcpy(cp, &val, alen); cp += alen; len += alen + 2; } /* * called station id */ if (called_id) { alen = (u_char)strlen(called_id); *cp++ = PW_CALLED; *cp++ = alen + 2; memcpy(cp, called_id, alen); cp += alen; len += alen + 2; } /* * calling station id */ if (calling_id) { alen = (u_char)strlen(calling_id); *cp++ = PW_CALLING; *cp++ = alen + 2; memcpy(cp, calling_id, alen); cp += alen; len += alen + 2; } ah->length = htons(len); if (acct_flag) { /* sign it */ /* STUB */ } if ((rc = sendto(fd, (char *)ah, len, 0, (struct sockaddr *)&addr, sizeof addr)) < 0) { perror("sendto: "); rad_exit(errno); } FD_ZERO(&fdset); FD_SET(fd, &fdset); tv.tv_sec = 5; tv.tv_usec = 0; if (select(32, &fdset, NULL, NULL, &tv) < 0) { perror("select: "); rad_exit(errno); } if (FD_ISSET(fd, &fdset)) { sa_len = sizeof sa_ihd; if ((rc = recvfrom(fd, r_buf, sizeof r_buf, 0, (struct sockaddr *)&sa_ihd, &sa_len)) < 0) { perror("recvfrom: "); rad_exit(errno); } } else { printf("recv timeout\n"); rad_exit(errno); } /* STUB - does not check response authenticator yet */ ah = (AUTH_HDR *)r_buf; switch(ah->code) { case PW_AUTHENTICATION_ACK: printf("Received Accept\n"); break; case PW_AUTHENTICATION_REJECT: printf("Received Reject\n"); break; case PW_ACCESS_CHALLENGE: printf("Received Challenge\n"); break; case PW_ACCOUNTING_RESPONSE: printf("Received Accounting Response\n"); break; default: printf("ERROR Received code %d id %d length %d\n", ah->code, ah->id, ah->length); return; break; } authreq = radrecv(host, ntohs(lport), secret, r_buf, rc); if (authreq != (AUTH_REQ *)NULL) { pair = authreq->request; while (pair != (VALUE_PAIR *)NULL) { fputs("\t", stdout); fprint_attr_val(stdout, pair); fputs("\n", stdout); pair = pair->next; } } close(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(host, udp_port, secret, buffer, length) UINT4 host; u_short udp_port; char *secret; u_char *buffer; int length; { u_char *ptr; char *bufalloc(); AUTH_HDR *auth; int totallen; int attribute; int attrlen; int vendor; int vsa; int vsattrlen; DICT_ATTR *attr; DICT_ATTR *dict_attrget(); DICT_ATTR *dict_vsattrget(); UINT4 lvalue; VALUE_PAIR *first_pair; VALUE_PAIR *prev; VALUE_PAIR *pair; VALUE_PAIR *pairalloc(); AUTH_REQ *authreq; AUTH_REQ *reqalloc(); void hexdump(); void pairfree(); void rad_exit(); void reqfree(); if (length < AUTH_HDR_LEN) { /* too short to be real */ log_err("radrecv: runt packet of %d bytes from %s/%d\n", length, ipaddr2strp(host), udp_port); return ((AUTH_REQ *)NULL); } /* * Pre-allocate the new request data structure */ authreq = reqalloc("radrecv"); auth = (AUTH_HDR *)buffer; totallen = ntohs(auth->length); if (totallen > length) { /* truncated packet, ignore */ log_err("radrecv: message from %s/%d claimed length %d, only %d bytes received\n", ipaddr2strp(host), udp_port, totallen, length); reqfree(authreq,"radrecv"); return((AUTH_REQ *)NULL); } DEBUG("message received from %s/%d.%d code=%d, length=%d\n", ipaddr2strp(host), udp_port, auth->id, auth->code, totallen); if (debug_flag > 1) { hexdump(buffer,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); strncpy(authreq->secret,secret,20); authreq->secret[19]='\0'; /* * 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 ( attrlen > AUTH_STRING_LEN ) { log_err("radrecv: attribute %d from %s too long, length of %d > %d\n", attribute, req2strp(authreq), attrlen, AUTH_STRING_LEN); reqfree(authreq,"radrecv"); return((AUTH_REQ *)NULL); } pair = pairalloc("radrecv"); if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) { sprintf(pair->name,"Unknown-%d",attribute); pair->attribute = attribute; pair->type = PW_TYPE_STRING; } else { strcpy(pair->name, attr->name); pair->attribute = attr->value; pair->type = attr->type; } if (pair->attribute == PW_VENDOR) { if (attrlen < 6 || ((vsattrlen = *(ptr+5)) != (attrlen-4))) { pair->vendor = 0; pair->vsattribute = 0; pair->type = PW_TYPE_STRING; } else { memcpy(&vendor, ptr, sizeof(UINT4)); vendor = ntohl(vendor); ptr += 4; vsa = *ptr++; attrlen = vsattrlen - 2; ptr++; length -= 6; pair->vendor = vendor; pair->vsattribute = vsa; if((attr = dict_vsattrget(vendor,vsa)) != (DICT_ATTR *)NULL) { strcpy(pair->name, attr->name); pair->type = attr->type; } else { sprintf(pair->name,"Vendor-Specific-%d-%d",vendor,vsa); pair->type = PW_TYPE_STRING; } } } switch(pair->type) { case PW_TYPE_STRING: memcpy(pair->strvalue, ptr, attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: memcpy(&lvalue, ptr, sizeof(UINT4)); pair->lvalue = ntohl(lvalue); 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); pairfree(pair,"radrecv"); break; } ptr += attrlen; length -= attrlen + 2; } authreq->request = first_pair; authreq->timestamp = now; /* now was set in main() */ /* copy the packet */ authreq->packet = bufalloc(totallen,"radrecv"); memcpy(authreq->packet,buffer,totallen); return(authreq); } void usage() { printf("usage: %s -d called_id -f -g calling_id -h -i id -p port -s secret -t type -v -x -u username password\n", progname); exit(0); } void rad_exit(rc) int rc; { exit(rc); } void version() { printf("%s 1.0 1999/6/15\n", progname); exit(0); }