diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..c2799bc --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,20 @@ +name: Publish release +on: + push: + tags: + - v*.* +jobs: + publush-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: install libgcrypt + run: sudo apt-get install -y libgcrypt-dev + - name: build + run: make + - name: release + uses: softprops/action-gh-release@v1 + with: + files: | + radiusplugin.so + radiusplugin.cnf diff --git a/.gitignore b/.gitignore index 5f23db7..c462dce 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.so *~ main +.vscode/ +.DS_Store \ No newline at end of file diff --git a/AccountingProcess.cpp b/AccountingProcess.cpp index c376de5..14af2bd 100644 --- a/AccountingProcess.cpp +++ b/AccountingProcess.cpp @@ -30,19 +30,16 @@ * @param context The plugin context as object from the class PluginContext. */ -void AccountingProcess::Accounting(PluginContext * context) +void AccountingProcess::Accounting(PluginContext *context) { - UserAcct *user=NULL; //The user for acconting. - int command, //The command from foreground process. - result; //The result from the socket. - string key; //The unique key. - AcctScheduler scheduler; //The scheduler for the accounting. - fd_set set; //A set for the select function. - struct timeval tv; //A timeinterval for the select function. - uint64_t bytesin=0, bytesout=0; - - - + UserAcct *user = NULL; //The user for acconting. + int command, //The command from foreground process. + result; //The result from the socket. + string key; //The unique key. + AcctScheduler scheduler; //The scheduler for the accounting. + fd_set set; //A set for the select function. + struct timeval tv; //A timeinterval for the select function. + uint64_t bytesin = 0, bytesout = 0; //Tell the parent everythink is ok. try @@ -51,58 +48,57 @@ void AccountingProcess::Accounting(PluginContext * context) } catch (Exception &e) { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT:" << e <<"\n"; + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT:" << e << "\n"; goto done; } - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Started, RESPONSE_INIT_SUCCEEDED was sent to Foreground Process.\n"; - // Event loop while (1) { //create the informations for the result function tv.tv_sec = 0; - tv.tv_usec = 500000; //wait 0,5s - FD_ZERO(&set); // clear out the set - FD_SET(context->acctsocketforegr.getSocket(), &set); // wait only on the socket from the foreground process + tv.tv_usec = 500000; //wait 0,5s + FD_ZERO(&set); // clear out the set + FD_SET(context->acctsocketforegr.getSocket(), &set); // wait only on the socket from the foreground process result = select(FD_SETSIZE, &set, NULL, NULL, &tv); //if there is a data on the socket - if (result>0) + if (result > 0) { // get a command from foreground process command = context->acctsocketforegr.recvInt(); - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Get a command.\n"; switch (command) { - //add a new user to the scheduler + //add a new user to the scheduler case ADD_USER: try { - - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New User.\n"; - // if accounting errors are non fatal return success and proceed with accounting - if(context->conf.getNonFatalAccounting()==true) - context->acctsocketforegr.send(RESPONSE_SUCCEEDED); - try{ - //allocate memory - user= new UserAcct; - } - catch(...) - { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for UserAcct." << endl; - } + // if accounting errors are non fatal return success and proceed with accounting + if (context->conf.getNonFatalAccounting() == true) + context->acctsocketforegr.send(RESPONSE_SUCCEEDED); + try + { + //allocate memory + user = new UserAcct; + } + catch (...) + { + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for UserAcct." << endl; + } //get the information from the foreground process user->setUsername(context->acctsocketforegr.recvStr()); - user->setSessionId(context->acctsocketforegr.recvStr()) ; - user->setDev(context->acctsocketforegr.recvStr()) ; + user->setSessionId(context->acctsocketforegr.recvStr()); + user->setDev(context->acctsocketforegr.recvStr()); user->setPortnumber(context->acctsocketforegr.recvInt()); user->setCallingStationId(context->acctsocketforegr.recvStr()); user->setFramedIp(context->acctsocketforegr.recvStr()); @@ -115,35 +111,33 @@ void AccountingProcess::Accounting(PluginContext * context) user->setStatusFileKey(context->acctsocketforegr.recvStr()); user->setUntrustedPort(context->acctsocketforegr.recvStr()); context->acctsocketforegr.recvBuf(user); - if (DEBUG (context->getVerbosity())) - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New user acct: username: " << user->getUsername() << ", interval: " << user->getAcctInterimInterval() << ", calling station: " << user->getCallingStationId() << ", commonname: " << user->getCommonname() << ", framed ip: " << user->getFramedIp() << ", framed ipv6: " << user->getFramedIp6() <<".\n"; - + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New user acct: username: " << user->getUsername() << ", interval: " << user->getAcctInterimInterval() << ", calling station: " << user->getCallingStationId() << ", commonname: " << user->getCommonname() << ", framed ip: " << user->getFramedIp() << ", framed ipv6: " << user->getFramedIp6() << ".\n"; //set the starttime user->setStarttime(time(NULL)); //calculate the nextupdate - user->setNextUpdate(user->getStarttime()+user->getAcctInterimInterval()); + user->setNextUpdate(user->getStarttime() + user->getAcctInterimInterval()); //send the start packet - if (user->sendStartPacket(context)==0) + if (user->sendStartPacket(context) == 0) { - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Start packet was send.\n"; - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: User was added to accounting scheduler.\n"; //set the system routes user->addSystemRoutes(context); - string script = context->conf.getVsaScript(); //execute vendor specific attribute script if (script.length() > 0) { - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Call vendor specific attribute script.\n"; if (callVsaScript(context, user, 1, 0) != 0) { @@ -153,10 +147,9 @@ void AccountingProcess::Accounting(PluginContext * context) //add the user to the scheduler scheduler.addUser(user); - //send the ok to the parent process - if(context->conf.getNonFatalAccounting()==false) - context->acctsocketforegr.send(RESPONSE_SUCCEEDED); - + //send the ok to the parent process + if (context->conf.getNonFatalAccounting() == false) + context->acctsocketforegr.send(RESPONSE_SUCCEEDED); } else { @@ -164,18 +157,16 @@ void AccountingProcess::Accounting(PluginContext * context) //user->deleteCcdFile(context); //tell the parent parent process something is wrong throw Exception("Accounting failed.\n"); - } // free the user, he was copied to the accounting scheduler list - } catch (Exception &e) { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: "<< e << "!\n"; - if(context->conf.getNonFatalAccounting()==false) - context->acctsocketforegr.send(RESPONSE_FAILED); + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: " << e << "!\n"; + if (context->conf.getNonFatalAccounting() == false) + context->acctsocketforegr.send(RESPONSE_FAILED); //close the background process, if the ipc socket is bad - if (e.getErrnum()==Exception::SOCKETSEND || e.getErrnum()==Exception::SOCKETRECV) + if (e.getErrnum() == Exception::SOCKETSEND || e.getErrnum() == Exception::SOCKETRECV) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Error in socket!\n"; goto done; @@ -183,8 +174,8 @@ void AccountingProcess::Accounting(PluginContext * context) } catch (...) { - if(context->conf.getNonFatalAccounting()==false) - context->acctsocketforegr.send(RESPONSE_FAILED); + if (context->conf.getNonFatalAccounting() == false) + context->acctsocketforegr.send(RESPONSE_FAILED); cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Unknown Exception!\n"; } delete user; @@ -193,25 +184,25 @@ void AccountingProcess::Accounting(PluginContext * context) //delete a user case DEL_USER: - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Delete user from accounting.\n"; - // Always send successful response since we don't care about disconnection status - // and we don't want to stale main thread. + // Always send successful response since we don't care about disconnection status + // and we don't want to stale main thread. context->acctsocketforegr.send(RESPONSE_SUCCEEDED); - + //receive the information try { - key=context->acctsocketforegr.recvStr(); - bytesout = strtoull(context->acctsocketforegr.recvStr().c_str(),NULL,10); - bytesin = strtoull(context->acctsocketforegr.recvStr().c_str(),NULL,10); + key = context->acctsocketforegr.recvStr(); + bytesout = strtoull(context->acctsocketforegr.recvStr().c_str(), NULL, 10); + bytesin = strtoull(context->acctsocketforegr.recvStr().c_str(), NULL, 10); } catch (Exception &e) { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: "<< e << "!\n"; + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: " << e << "!\n"; //close the background process, if the ipc socket is bad - if (e.getErrnum()==Exception::SOCKETSEND || e.getErrnum()==Exception::SOCKETRECV) + if (e.getErrnum() == Exception::SOCKETSEND || e.getErrnum() == Exception::SOCKETRECV) { goto done; } @@ -222,13 +213,12 @@ void AccountingProcess::Accounting(PluginContext * context) } //find the user, he must be already there - user=scheduler.findUser(key.c_str()); + user = scheduler.findUser(key.c_str()); - if (user) { - if (DEBUG (context->getVerbosity())) - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Stop acct: username: " << user->getUsername()<< ", calling station: " << user->getCallingStationId()<< ", commonname: " << user->getCommonname() << ".\n"; + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Stop acct: username: " << user->getUsername() << ", calling station: " << user->getCallingStationId() << ", commonname: " << user->getCommonname() << ".\n"; //delete the system routes user->delSystemRoutes(context); @@ -241,7 +231,7 @@ void AccountingProcess::Accounting(PluginContext * context) if (script.length() > 0) { //string command= context->conf.getVsaScript() + string(" ") + string("ACTION=CLIENT_CONNECT")+string(" ")+string("USERNAME=")+user->getUsername()+string(" ")+string("COMMONNAME=")+user->getCommonname()+string(" ")+string("UNTRUSTED_IP=")+user->getCallingStationId() + string(" ") + string("UNTRUSTED_PORT=") + user->getUntrustedPort() + user->getVsaString(); - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: Call vendor specific attribute script.\n"; if (callVsaScript(context, user, 2, 0) != 0) { @@ -252,16 +242,15 @@ void AccountingProcess::Accounting(PluginContext * context) try { //delete the user from the accounting scheduler - user->setBytesIn(bytesin & 0xFFFFFFFF); - user->setBytesOut(bytesout & 0xFFFFFFFF); - user->setGigaIn(bytesin >> 32); - user->setGigaOut(bytesout >> 32); + user->setBytesIn(bytesin & 0xFFFFFFFF); + user->setBytesOut(bytesout & 0xFFFFFFFF); + user->setGigaIn(bytesin >> 32); + user->setGigaOut(bytesout >> 32); scheduler.delUser(context, user); - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: User with key: " << key << " was deleted from accounting.\n"; - } catch (Exception &e) { @@ -275,14 +264,13 @@ void AccountingProcess::Accounting(PluginContext * context) } else { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: No user with this key "<< key <<".\n"; - + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: No user with this key " << key << ".\n"; } break; //exit the loop case COMMAND_EXIT: - if (DEBUG (context->getVerbosity())) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Get command exit.\n"; goto done; @@ -291,15 +279,12 @@ void AccountingProcess::Accounting(PluginContext * context) break; default: - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND: unknown command code: code= "<< command <<", exiting.\n"; + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND: unknown command code: code= " << command << ", exiting.\n"; goto done; - - } } //after 0,5sec without a command call the scheduler scheduler.doAccounting(context); - } done: //end the process @@ -330,160 +315,160 @@ void AccountingProcess::Accounting(PluginContext * context) * @return -1 in case of error, else 0 */ -int AccountingProcess::callVsaScript(PluginContext * context, User * user, unsigned int action, unsigned int rekeying) +int AccountingProcess::callVsaScript(PluginContext *context, User *user, unsigned int action, unsigned int rekeying) { - char * route; - Octet * buf; + char *route; + Octet *buf; int buflen = 3 * sizeof(int); if (user->getUsername().length() != 0) { - buflen=buflen+user->getUsername().length()+2*sizeof(int); + buflen = buflen + user->getUsername().length() + 2 * sizeof(int); } if (user->getCommonname().length() != 0) { - buflen=buflen+user->getCommonname().length()+2*sizeof(int); + buflen = buflen + user->getCommonname().length() + 2 * sizeof(int); } if (user->getFramedIp().length() != 0) { - buflen=buflen+user->getFramedIp().length()+2*sizeof(int); + buflen = buflen + user->getFramedIp().length() + 2 * sizeof(int); } if (user->getCallingStationId().length() != 0) { - buflen=buflen+user->getCallingStationId().length()+2*sizeof(int); + buflen = buflen + user->getCallingStationId().length() + 2 * sizeof(int); } if (user->getUntrustedPort().length() != 0) { - buflen=buflen+user->getUntrustedPort().length()+2*sizeof(int); + buflen = buflen + user->getUntrustedPort().length() + 2 * sizeof(int); } if (user->getVsaBufLen() != 0) { - buflen=buflen+user->getVsaBufLen() +2*sizeof(int); + buflen = buflen + user->getVsaBufLen() + 2 * sizeof(int); } - char routes[user->getFramedRoutes().length()+1]; + char routes[user->getFramedRoutes().length() + 1]; strncpy(routes, user->getFramedRoutes().c_str(), user->getFramedRoutes().length()); - routes[user->getFramedRoutes().length()]=0; - if ((route = strtok(routes,";")) != NULL) + routes[user->getFramedRoutes().length()] = 0; + if ((route = strtok(routes, ";")) != NULL) { - buflen=buflen+strlen(route)+2*sizeof(int); - while ((route = strtok(NULL,";"))!= NULL) + buflen = buflen + strlen(route) + 2 * sizeof(int); + while ((route = strtok(NULL, ";")) != NULL) { - buflen=buflen+strlen(route)+2*sizeof(int); + buflen = buflen + strlen(route) + 2 * sizeof(int); } } - try{ - buf = new Octet[buflen]; + try + { + buf = new Octet[buflen]; } - catch(...) + catch (...) { - cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for framedroutes buf." << endl; + cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for framedroutes buf." << endl; } - unsigned int value = htonl(action); - memcpy(buf,&value, 4); + unsigned int value = htonl(action); + memcpy(buf, &value, 4); value = htonl(rekeying); - memcpy(buf+4,&value, 4); + memcpy(buf + 4, &value, 4); value = htonl(buflen); - memcpy(buf+8,&value, 4); + memcpy(buf + 8, &value, 4); - int i=12; + int i = 12; if (user->getUsername().length() != 0) { value = htonl(101); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getUsername().length()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy( buf+i, user->getUsername().c_str(),user->getUsername().length()); - i=i+user->getUsername().length(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getUsername().c_str(), user->getUsername().length()); + i = i + user->getUsername().length(); } if (user->getCommonname().length() != 0) { value = htonl(102); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getCommonname().length()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy( buf+i, user->getCommonname().c_str(),user->getCommonname().length()); - i=i+user->getCommonname().length(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getCommonname().c_str(), user->getCommonname().length()); + i = i + user->getCommonname().length(); } if (user->getFramedIp().length() != 0) { value = htonl(103); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getFramedIp().length()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy( buf+i, user->getFramedIp().c_str(),user->getFramedIp().length()); - i=i+user->getFramedIp().length(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getFramedIp().c_str(), user->getFramedIp().length()); + i = i + user->getFramedIp().length(); } if (user->getCallingStationId().length() != 0) { value = htonl(104); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getCallingStationId().length()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy( buf+i, user->getCallingStationId().c_str(),user->getCallingStationId().length()); - i=i+user->getCallingStationId().length(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getCallingStationId().c_str(), user->getCallingStationId().length()); + i = i + user->getCallingStationId().length(); } if (user->getUntrustedPort().length() != 0) { value = htonl(105); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getUntrustedPort().length()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy( buf+i, user->getUntrustedPort().c_str(),user->getUntrustedPort().length()); - i=i+user->getUntrustedPort().length(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getUntrustedPort().c_str(), user->getUntrustedPort().length()); + i = i + user->getUntrustedPort().length(); } strncpy(routes, user->getFramedRoutes().c_str(), user->getFramedRoutes().length()); - routes[user->getFramedRoutes().length()]=0; - if ((route = strtok(routes,";")) != NULL) + routes[user->getFramedRoutes().length()] = 0; + if ((route = strtok(routes, ";")) != NULL) { value = htonl(106); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(strlen(route)); - memcpy(buf+i,&value, 4); - i+=4; - memcpy(buf+i, route, strlen(route)); - i=i+strlen(route); - while ((route = strtok(NULL,";"))!= NULL) + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, route, strlen(route)); + i = i + strlen(route); + while ((route = strtok(NULL, ";")) != NULL) { value = htonl(106); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(strlen(route)); - memcpy(buf+i,&value, 4); - i+=4; - memcpy(buf+i, route, strlen(route)); - i=i+strlen(route); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, route, strlen(route)); + i = i + strlen(route); } } if (user->getVsaBufLen() != 0) { value = htonl(107); - memcpy(buf+i,&value, 4); - i+=4; + memcpy(buf + i, &value, 4); + i += 4; value = htonl(user->getVsaBufLen()); - memcpy(buf+i,&value, 4); - i+=4; - memcpy(buf+i, user->getVsaBuf(),user->getVsaBufLen()); - i=i+user->getVsaBufLen(); + memcpy(buf + i, &value, 4); + i += 4; + memcpy(buf + i, user->getVsaBuf(), user->getVsaBufLen()); + i = i + user->getVsaBufLen(); } - - if (mkfifo(context->conf.getVsaNamedPipe().c_str(), 0600)== -1) + if (mkfifo(context->conf.getVsaNamedPipe().c_str(), 0600) == -1) { /* FIFO bereits vorhanden - kein fataler Fehler */ if (errno == EEXIST) @@ -492,35 +477,33 @@ int AccountingProcess::callVsaScript(PluginContext * context, User * user, unsig } else { - cerr << getTime() <<"RADIUS-PLUGIN: Error in mkfifio()"; + cerr << getTime() << "RADIUS-PLUGIN: Error in mkfifio()"; return -1; } } - int fd_fifo=open(context->conf.getVsaNamedPipe().c_str(), O_RDWR | O_NONBLOCK); + int fd_fifo = open(context->conf.getVsaNamedPipe().c_str(), O_RDWR | O_NONBLOCK); if (fd_fifo == -1) { - cerr << getTime() <<"RADIUS-PLUGIN: Error in opening pipe to VSAScript."; + cerr << getTime() << "RADIUS-PLUGIN: Error in opening pipe to VSAScript."; return -1; } - string exe=string(context->conf.getVsaScript()) + " " + string(context->conf.getVsaNamedPipe()); - if (write (fd_fifo, buf, buflen) != buflen) + string exe = string(context->conf.getVsaScript()) + " " + string(context->conf.getVsaNamedPipe()); + if (write(fd_fifo, buf, buflen) != buflen) { close(fd_fifo); - cerr << getTime() << "RADIUS-PLUGIN: Could not write in Pipe to VSAScript!"; + cerr << getTime() << "RADIUS-PLUGIN: Could not write in Pipe to VSAScript!"; return -1; } - if (system(exe.c_str())!=0) + if (system(exe.c_str()) != 0) { close(fd_fifo); - cerr << getTime() << "RADIUS-PLUGIN: Error in VSAScript!"; + cerr << getTime() << "RADIUS-PLUGIN: Error in VSAScript!"; return -1; } close(fd_fifo); - delete [] buf; + delete[] buf; return 0; } - - diff --git a/ChangeLog b/CHANGELOG.md similarity index 75% rename from ChangeLog rename to CHANGELOG.md index 483e512..8947357 100644 --- a/ChangeLog +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ -radiusplugin_v1.1a: +# CHANGELOG + +## v1.1a: - Standard configfile: /etc/openvon/radiusplugin.cnf. - #include in IpcSocket.h, is needed for compiling on some systems. - Correct README: A configfile must set with "-c configfile". - Correct fprintf statement in UserAuth.cpp (line 300). - Set debug level from 7 to 5. -radiusplugin_v1.1.a (5.1.06): +## v1.1.a (5.1.06): - correct calculate of ipaddress for ifconfig-push in UserAuth::createCcdFile() - FramedIpAddress-Attribute is set to the IP address OpenVPN is assigned to the client, the address is read from ENVP-Array with name ifconfig_pool_remote_ip and set to @@ -15,47 +17,48 @@ radiusplugin_v1.1.a (5.1.06): - add parameters in config with getters, setters and modify constructors and destructor - modify UserAuth::createCcdFile to set the right topology option -radiusplugin_1.2: +## 1.2: - use libgcrypt instead openssl (for gpl compability) - checking of the authenticator field from received packets - correct error on deleting users without an acct-interim-interval in AcctScheduler.cpp::findUser() - send message to foreground process if no user was found (else the plugin hangs) -radiusplugin_1.2a: +## 1.2a: - correct error if a user connects again, if he is still known by the plugin, now the user can't reconnect if he is known by the plugin - Change GPL text - add COPYING file -radiusplugin_1.2b: +## 1.2b: - correct error: don't disconnect at rekeying/renegotiation -radiusplugin_1.2c: +## 1.2c: - new algorithm to generate Acct-Session-ID, so it should be unique ever, see createSessionID in radiusplugin.cpp -radiusplugin_1.2d: +## 1.2d: - option in OpenVPN config file is now: "plugin /etc/openvpn/radiusplugin.so [configfile] - correct bug: close of configfile was missing -radiusplugin_1.2e: +## 1.2e: - use RadiusClass_v1.1a for more machine independance (big endian/little endian) - use send()/recvInt() instead of sendCode()/recvCode() for internal socket communication -radiusplugin_2.0: -- new features: - - support of vendor specific attributes with example perl script - - support for OpenVPN options: dupliate-cn, client-cert-not-required, username-as-commonname - - comments allowd in config file - - parsing of OpenVPN config file for options (see radiusplugin.cnf) -- fixed bugs: - - crash on rekeying if no radius server respond - - framed ip is sent in access request packets if the client already has one (at rekeying), for dynamical key assignment through the radius server, so it will not send a new one +## 2.0: +new features +- support of vendor specific attributes with example perl script +- support for OpenVPN options: dupliate-cn, client-cert-not-required, username-as-commonname +- comments allowd in config file +- parsing of OpenVPN config file for options (see radiusplugin.cnf) + +fixed bugs +- crash on rekeying if no radius server respond +- framed ip is sent in access request packets if the client already has one (at rekeying), for dynamical key assignment through the radius server, so it will not send a new one - passwords and sharedsecret are shown as ****** in the logs -radiusplugin_2.0a: -- fix error in IpcSocket.h, - bad function name: void IpcSocket::recvBuf(User *) -> void recvBuf(User *); +## 2.0a: +- fix error in IpcSocket.h, +bad function name: void IpcSocket::recvBuf(User *) -> void recvBuf(User *); -radiusplugin_2.0b: +## 2.0b: - fixed datatypes from int to uint32_t/uint64_t to prevent overflows above 2^31 - added support for gigawords - added a Makefile @@ -69,21 +72,21 @@ radiusplugin_2.0b: - don't delete client config file at CLIENT-DISCONNECT it could be already a new file from a new AUTH-USER-PASS-VERIFY - Fix segmentation fault in radiusplugin.cpp. The error occurs if an accounting request fails after a successful authentication. -radiusplugin_2.0c: +## 2.0c: - add IPv6 support (IPv6 patch for OpenVPN openvpn-2.1-udp6.patch) - check if string/buffer length is zero in IpcSocket - delete NAS port if authentication fails -radiusplugin_2.0d_beta: +## 2.0d_beta: - add some headerfiles to avoid compiling errors on Fedora with gcc4 - close socket in radiuspacket.cpp on retries - add parenthesize to avoid compiler warnings -radiusplugin_2.1: -- Support of OPENVPN_PLUGIN_FUNC_DEFERRED: -*** The communication to the radius server for authentication is outsourced in a thread. -*** If an auth control file is defined and "useauthcontrolfile=true" is defined in the config file of plugin the authentication is done the background. -*** The OpenVPN process needs write permission in the OpenVPN directory. +## 2.1: +- Support of OPENVPN_PLUGIN_FUNC_DEFERRED: +The communication to the radius server for authentication is outsourced in a thread. +If an auth control file is defined and `useauthcontrolfile=true` is defined in the config file of plugin the authentication is done the background. +The OpenVPN process needs write permission in the OpenVPN directory. - Timestamps are included in the debug information. - Adapted to OPENVPN_PLUGIN_VERSION 2 - Internal key is now based on untrusted_ip and untrusted_before. @@ -98,6 +101,12 @@ radiusplugin_2.1: - Add NAS port number to the session id, it's definitely unique - Acct-Session-Id added to Access-Request packet (RFC2866) -radiusplugin_2.1a: +## 2.1a: - Implement accounting only feature (option: accountingonly, default false) -- Implement non fatal accounting (failures during accounting let the user still connect) (nonfatalaccounting) \ No newline at end of file +- Implement non fatal accounting (failures during accounting let the user still connect) (nonfatalaccounting) + +## 2.2 +- Fix locks for operations with users waiting for authentication or accounting start + +## 2.3: +- Allow to disable accounting \ No newline at end of file diff --git a/Config.cpp b/Config.cpp index 135cd35..fd846e2 100644 --- a/Config.cpp +++ b/Config.cpp @@ -1,7 +1,7 @@ /* - * radiusplugin -- An OpenVPN plugin for do radius authentication + * radiusplugin -- An OpenVPN plugin for do radius authentication * and accounting. - * + * * Copyright (C) 2005 EWE TEL GmbH/Ralf Luebben * * This program is free software; you can redistribute it and/or modify @@ -28,12 +28,12 @@ Config::Config(void) { - + this->usernameascommonname=false; this->clientcertnotrequired=false; this->overwriteccfiles=true; - this->useauthcontrolfile=false; - this->useclientconnectdeferfile=false; + this->useauthcontrolfile=false; + this->useclientconnectdeferfile=false; this->accountingonly=false; this->nonfatalaccounting=false; this->defacctinteriminterval=0; @@ -41,6 +41,7 @@ Config::Config(void) this->openvpnconfig=""; this->vsanamedpipe=""; this->vsascript=""; + this->accountingenabled=true; memset(this->subnet,0,16); memset(this->p2p,0,16); memset(this->p2p6,0,40); @@ -63,36 +64,37 @@ Config::Config(char * configfile) this->vsascript=""; this->usernameascommonname=false; this->clientcertnotrequired=false; - this->overwriteccfiles=true; - this->useauthcontrolfile=false; - this->useclientconnectdeferfile=false; + this->overwriteccfiles=true; + this->useauthcontrolfile=false; + this->accountingenabled=true; + this->useclientconnectdeferfile=false; this->accountingonly=false; this->nonfatalaccounting=false; this->defacctinteriminterval=0; this->parseConfigFile(configfile); - + } /** The destructur clears the serverlist. */ Config::~Config(void) { - - + + } -/** The method parse the configfile for attributes and +/** The method parse the configfile for attributes and * radius server, the attributes are copied to the * member variables. * @param configfile The name of the configfile. - * @return An integer, 0 if everything is ok + * @return An integer, 0 if everything is ok * or PARSING_ERROR or BAD_FILE if something is wrong.*/ int Config::parseConfigFile(const char * configfile) { string line; - + ifstream file; file.open(configfile, ios::in); if (file.is_open()) @@ -105,16 +107,16 @@ int Config::parseConfigFile(const char * configfile) { if (strncmp(line.c_str(),"subnet=",7)==0) { - if((line.size()-7)>15) + if ((line.size()-7)>15) { return BAD_FILE; } line.copy(this->subnet,line.size()-7,7); - + } if (strncmp(line.c_str(),"p2p=",4)==0) { - if((line.size()-4)>15) + if ((line.size()-4)>15) { return BAD_FILE; } @@ -122,7 +124,7 @@ int Config::parseConfigFile(const char * configfile) } if (strncmp(line.c_str(),"p2p6=",5)==0) { - if((line.size()-5)>39) + if ((line.size()-5)>39) { return BAD_FILE; } @@ -136,34 +138,29 @@ int Config::parseConfigFile(const char * configfile) { this->vsanamedpipe=line.substr(13,line.size()-13); } - + if (strncmp(line.c_str(),"OpenVPNConfig=",14)==0) { this->openvpnconfig=line.substr(14,line.size()-14); } if (strncmp(line.c_str(),"overwriteccfiles=",17)==0) { - string stmp=line.substr(17,line.size()-17); deletechars(&stmp); if(stmp == "true") this->overwriteccfiles=true; else if (stmp =="false") this->overwriteccfiles=false; else return BAD_FILE; - } - if (strncmp(line.c_str(),"useauthcontrolfile=",19)==0) + if (strncmp(line.c_str(),"useauthcontrolfile=",19)==0) { - string stmp=line.substr(19,line.size()-19); deletechars(&stmp); if(stmp == "true") this->useauthcontrolfile=true; else if (stmp =="false") this->useauthcontrolfile=false; else return BAD_FILE; - } - if (strncmp(line.c_str(),"useclientconnectdeferfile=",26)==0) + if (strncmp(line.c_str(),"useclientconnectdeferfile=",26)==0) { - string stmp=line.substr(26,line.size()-26); deletechars(&stmp); if(stmp == "true") this->useclientconnectdeferfile=true; @@ -173,27 +170,31 @@ int Config::parseConfigFile(const char * configfile) } if (strncmp(line.c_str(),"accountingonly=",15)==0) { - + string stmp=line.substr(15,line.size()-15); deletechars(&stmp); if(stmp == "true") this->accountingonly=true; else if (stmp =="false") this->accountingonly=false; else return BAD_FILE; - } if (strncmp(line.c_str(),"nonfatalaccounting=",19)==0) { - string stmp=line.substr(19,line.size()-19); deletechars(&stmp); if(stmp == "true") this->nonfatalaccounting=true; else if (stmp =="false") this->nonfatalaccounting=false; else return BAD_FILE; - + } + if (strncmp(line.c_str(),"accountingenabled=",18)==0) + { + string stmp=line.substr(18,line.size()-18); + deletechars(&stmp); + if(stmp == "true") this->accountingenabled=true; + else if (stmp =="false") this->accountingenabled=false; + else return BAD_FILE; } if (strncmp(line.c_str(),"defacctinteriminterval=",23)==0) { - string stmp=line.substr(23,line.size()-23); deletechars(&stmp); char *stemp; @@ -204,12 +205,12 @@ int Config::parseConfigFile(const char * configfile) this->defacctinteriminterval=(int)defacctinteriminterval; } } - + } file.close(); // if the main files contains references to other config files // we don't need to care about recursive includes, OpenVPN does it already - list configfiles; + list configfiles; configfiles.push_back(this->openvpnconfig); //open OpenVPN config while(configfiles.size() > 0) @@ -223,7 +224,7 @@ int Config::parseConfigFile(const char * configfile) while(file2.eof()==false) { getline(file2,line); - + if(line.empty()==false) { string param=line; @@ -264,7 +265,7 @@ int Config::parseConfigFile(const char * configfile) { //method deletechars don't work, entry has formet: status [time] pos = line.find_first_of("#"); - if (pos != string::npos) + if (pos != string::npos) { line.erase(pos); } @@ -281,10 +282,10 @@ int Config::parseConfigFile(const char * configfile) this->deletechars(&line); if(!line.empty()) { - + this->statusfile=line; } - } + } } } file.close(); @@ -302,39 +303,39 @@ int Config::parseConfigFile(const char * configfile) } return 0; } - - + + /** The method deletes chars from a string. * This is used to delete tabs, spaces, # and '\0' * from a string. * @param text The string which should be cleaned. - */ + */ void Config::deletechars(string * line) { char const* delims = " \t\r\n\0"; - + // trim leading whitespace string::size_type pos = line->find_first_not_of(delims); if (pos != string::npos) line->erase(0,pos ); // trim trailing whitespace - pos = line->find_last_not_of(delims); + pos = line->find_last_not_of(delims); if (pos != string::npos) line->erase(pos+1); - + //trim whitespaces in line pos = line->find_first_of(delims); - while (pos != string::npos) + while (pos != string::npos) { line->erase(pos,1); pos = line->find_first_of(delims); } - + // trim comments pos = line->find_first_of("#"); - if (pos != string::npos) + if (pos != string::npos) { line->erase(pos); } - + } @@ -362,7 +363,7 @@ void Config::getValue(const char * text, char * value) - + /** The getter methid for the client config dir (ccd). * @return A string to the ccd. */ @@ -379,7 +380,7 @@ void Config::setCcdPath(string path) { if(path[path.length()]!= '/') { - path +='/'; + path +='/'; } this->ccdPath=path; } @@ -397,13 +398,13 @@ string Config::getStatusFile(void) */ void Config::setStatusFile(string file) { - + this->statusfile=file; } /** The setter method for the nas ip address. * @param ip A string with ip address. - */ + */ void Config::setSubnet(char * ip) { strncpy(this->subnet,ip, 16); @@ -420,7 +421,7 @@ char * Config::getSubnet(void) /** The setter method for the p2p address. * @param ip A string with p2p address. - */ + */ void Config::setP2p(char * ip) { strncpy(this->p2p,ip, 16); @@ -437,7 +438,7 @@ char * Config::getP2p(void) /** The setter method for the p2p6 address. * @param ip A string with p2p6 address. - */ + */ void Config::setP2p6(char * ip) { strncpy(this->p2p6,ip, 40); @@ -454,7 +455,7 @@ char * Config::getP2p6(void) /** The setter method for the vsascript. * @param script A path of the script. - */ + */ void Config::setVsaScript(string script) { this->vsascript=script; @@ -471,7 +472,7 @@ string Config::getVsaScript(void) /** The setter method for the usernameascommonname value. * @param b A boolean for option usernameascommonname. - */ + */ void Config::setUsernameAsCommonname(bool b) { this->usernameascommonname=b; @@ -488,7 +489,7 @@ bool Config::getUsernameAsCommonname(void) /** The setter method for the vsanamedpipe. * @param script A path of the pipe. - */ + */ void Config::setVsaNamedPipe(string pipe) { this->vsanamedpipe=pipe; @@ -506,7 +507,7 @@ string Config::getVsaNamedPipe(void) /** The setter method for the clientcertnotrequired value. * @param b A boolean for option clientcertnotrequired. - */ + */ void Config::setClientCertNotRequired(bool b) { this->clientcertnotrequired=b; @@ -526,7 +527,7 @@ bool Config::getClientCertNotRequired(void) */ string Config::getOpenVPNConfig(void) { - return this->openvpnconfig; + return this->openvpnconfig; } /** The setter method for the path to the OpenVPN config @@ -550,7 +551,7 @@ bool Config::getOverWriteCCFiles(void) */ void Config::setOverWriteCCFiles(bool overwrite) { - this->overwriteccfiles=overwrite; + this->overwriteccfiles=overwrite; } /** Getter method for the authcontrolfile variable. @@ -566,7 +567,7 @@ bool Config::getUseAuthControlFile(void) */ void Config::setUseAuthControlFile(bool b) { - this->useauthcontrolfile=b; + this->useauthcontrolfile=b; } /** Getter method for the clientconnectdeferfile variable. @@ -588,31 +589,41 @@ void Config::setUseClientConnectDeferFile(bool b) bool Config::getAccountingOnly(void) { - return this->accountingonly; + return this->accountingonly; } void Config::setAccountingOnly(bool b) { - this->accountingonly=b; + this->accountingonly=b; +} + +bool Config::getAccountingEnabled(void) +{ + return this->accountingenabled; +} + +void Config::setAccountingEnabled(bool b) +{ + this->accountingenabled=b; } bool Config::getNonFatalAccounting(void) { - return this->nonfatalaccounting; + return this->nonfatalaccounting; } void Config::setNonFatalAccounting(bool b) { - this->nonfatalaccounting=b; + this->nonfatalaccounting=b; } int Config::getDefAcctInterimInterval(void) { - return this->defacctinteriminterval; + return this->defacctinteriminterval; } void Config::setDefAcctInterimInterval(int b) { - this->defacctinteriminterval=b; + this->defacctinteriminterval=b; } diff --git a/Config.h b/Config.h index 693ef3f..3c0e834 100644 --- a/Config.h +++ b/Config.h @@ -1,7 +1,7 @@ /* - * radiusplugin -- An OpenVPN plugin for do radius authentication + * radiusplugin -- An OpenVPN plugin for do radius authentication * and accounting. - * + * * Copyright (C) 2005 EWE TEL GmbH/Ralf Luebben * * This program is free software; you can redistribute it and/or modify @@ -18,7 +18,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - + #ifndef _CONFIG_H_ #define _CONFIG_H_ @@ -30,18 +30,18 @@ #include "RadiusClass/error.h" #include -#include +#include using namespace std; -/**This class represents the configurations attributes (without radius configuration) which +/**This class represents the configurations attributes (without radius configuration) which * can set in the configuration file and methods for the attributes. */ class Config { private: - + string ccdPath; /** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - ------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------ -The plugin was developed during my diploma thesis for the EWETEL GmbH (www.ewetel.net). -They main idea was to create a virtualized SSL-VPN with RADIUS support. -The virtualization was done by XEN. -At this place I would like to thank the people from EWETEL GmbH and EWE AG (www.ewe.de) -who supported me with the required equipment, hardware -and helped me with all my questions. - -The plugin was developed and tested under Debian Sarge and OpenVPN 2.0 on x86-machines. ------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------ - - - -The RADIUS-Class: ------------------ -For this plugin was developed a RADIUS-Class in C++. It was based on the C-library FF RADIUS Library v0.1 from -Fabrizio Fiorucci (http://ffradiuslib.ff.homelinux.net). -If somebody is interested in a standalone RADIUS-class please contact me. There will be some little changes. - - -DOCUMENTATION: --------------- -The documentation is done with doxygen. -To generate the documentation execute "doxygen doxygen.conf" in the folder of the plugin. - - -INSTALL: --------- -1. Requirements: the libgcrypt library. - -2. Compile: see section COMPILE - -3. Set the plugin in the OpenVPN configfile: plugin /path/to/plugin/radiusplugin.so [configfile] - -4. Create a configfile for the plugin, see radiusplugin.cnf. If no path is set in the configfile of OpenVPN - the plugin will read the file /etc/openvpn/radiusplugin.cnf: . - The configuration is case-sensitive, for an example see radiusplugin.cnf. - -5. Set some configuration in the config file of the OpenVPN server: - - status /var/log/openvpn/status.log 1 # The status, where the plugin reads the accounting information from. - -6. Configure in the config file of the OpenVPN client: - - auth-user-pass [/etc/openvpn/passwd] # Set this for sending a username and password to the server, this values are the username and password for the radius authentication. - -7. If you use auth_control_file (>= OpenVPN 2.1 rc8), the plugin directory needs write permission to the OpenVPN directory or use the "--tmp-dir" option to define a directory for the files. - -8. Maybe write your own script for vendor specific attributes (see section VENDOR SPECIFIC ATTRIBUTES). - - -OTHER DOCUMENTATION -------------------- -An "OpenVPN - RADIUS - MySQL Howto" in german can be found at: -http://www.roessner-net.com/ - - -FREERADIUS EXAMPLE -------------------- -user1 Cleartext-Password := "testing" - Service-Type = Framed-User, - Framed-IP-Netmask = 255.255.255.0, - Framed-IP-Address = 10.8.0.33, - Framed-Routing = Broadcast-Listen, - Framed-Compression = Van-Jacobsen-TCP-IP, - Framed-Route += "192.168.101.0/26 10.8.0.1/32 1", - Framed-Route += "192.168.111.0/24 10.8.0.1/32 1", - Framed-Route += "192.168.112.0/24 10.8.0.1/32 1", - Acct-Interim-Interval=5, - Ascend-Data-Rate=100, - Ascend-Xmit-Rate=200, - Framed-Protocol = PPP - -The Framed-Route attribute must be in the format as shown above! - - -COMPILE --------- -(tested with Debian on x86, for other architectures see section TROUBLESHOOTING): - ->$ make - - -RADIUS PACKETS and OpenVPN events (see http://openvpn.net/man.html SCRIPTING AND ENVIRONMENTAL VARIABLES) ---------------------------------------------------------------------------------------------------------- -OpenVPN event RADIUS PACKET Information -------------- ------------ ------------ -auth-user-pass-verify ACCESS REQUEST parse of attributes (see below) -client-connect ACCOUNTING REQUEST (Start) -- ACCOUNTING REQUEST (Update) If the user had a acct interim interval attribute the update - is sent periodically with this interval.(Start and stop accounting - packets are sent ever.) -client-disconnect ACCOUNTING REQUEST (Stop) - - -RADIUS ATTRIBUTES WHICH PARSED BY THE PLUGIN AND WRITE TO THE CLIENT CONFIG FILE: ---------------------------------------------- -- Framed ip address -- Acct Interim Interval -- Framed Routes (only in following format, e.g.: "192.168.101.0/26 10.8.0.1/32 1") - - -OTHER RADIUS ATTRIBUTES ------------------------ -- Reply Message the text is forwarded to stderr - -VENDOR SPECIFIC ATTRIBUTES ---------------------------- -If you want use vendor specific attributes the plugin can call your own program or script at the OpenVPN action -CLIENT-CONNECT and CLIENT-DISCONNECT. -If you want to use the feature you have to specify the program and a named pipe for communication -in the config file. -The file vsascript.pl shows an example. - - -LIMITS: -------- -commonname of the certificate 256 characters -routes entry from the radius server 50 characters - - -LIMITS in RadiusConfig for the configfile: ------------------------------------------- -(The limit is the length of the array minus 1!) -char serviceType[2]; -char framedProtocol[2]; -char nasPortType[2]; -char nasIdentifier[128]; -char nasIpAddress[16]; -char p2p[16]; -char subnet[16]; - - - -TROUBLESHOOTING ---------------- -- BSD: - - RADIUS attributes FramedIP and FramedRoutes mandatory - - RADIUS attribute FramedIP is ignored - - Use gmake -f Makefile.bsd (maybe you have to adapt library and include path, it was tested for FreeBSD) - -- Setting of Framed-Routes fails under BSD: - - At the moment the plugin can only set the routes on Linux OSs. - - The system call for "route ... " must be changed. Please contact me for a patch. - - Newer version will integrate OS dependent calls. - -- OpenVPN hangs when a client connects: - - Check OpenVPN config and check the OpenVPN log (debuglevel 7): - - The plugin needs following varibales from OpenVPN in ENVP-array: - - username, password, untrusted_ip, common_name, untrusted_port, ifconfig_pool_remote_ip, verb - - In some cases ifconfig_pool_remote_ip is missing (Depends on OpenVPN config). - -- The radius packets are sent over the wrong interface: - - Sometimes on multi interface machines with IPSec the operating system doesn't find the right interface. - - Use the plugin from Magne J. Andreassen (it is for version 1.2a) - -- The plugin writes client config files without any entry: - - The plugin always writes the file for every connecting client, please set the ccd path - in the config file of the plugin to a existing dummy directory. - -- Compiling fails on Solaris 10: - - add following options: -lsocket -lnsl -lresolv - -- Values for BytesIn/BytesOut are 0 in the accounting log of the RADIUS server: - - Make sure that the status file of OpenVPN exists and the plugin can read it. - - Use version 1 for the status file. - -- Error at compiling: not initialised shared memory before the call to the MD5 function: - - Make sure that you use libgrypt 1.2.0 or higher. - -- Error at compiling: DBG: md_enable: algorithm -4195948 not available: - - Make sure that you use libgrypt 1.2.0 or higher. - -- The plugin can't write the auth_control_file. - - The plugin needs write permission in the OpenVPN directory. - - - -FOR TESTING ------------ - -Attention: Some values are hard coded in main.cpp testing, so you can get unexpected values when testing. -For example the framedip is always 10.8.0.100, because it is read from the environment variable "ifconfig_pool_remote_ip", which is passed -to the plugin from OpenVPN! -All hard coded variabled: -env1[0]="username=user1"; -env1[1]="password=testing"; -env1[2]="verb=10"; -env1[3]="untrusted_ip=127.0.0.1"; -env1[4]="common_name=R-VPNGateway1"; -env1[5]="trusted_ip=127.0.0.1"; -env1[6]="ifconfig_pool_remote_ip=10.8.0.100"; - -compile for test with a main function: -g++ -Wall -o main AccountingProcess.cpp Exception.cpp PluginContext.cpp UserAuth.cpp AcctScheduler.cpp IpcSocket.cpp radiusplugin.cpp User.cpp AuthenticationProcess.cpp main.cpp UserAcct.cpp UserPlugin.cpp Config.cpp RadiusClass/RadiusAttribute.cpp RadiusClass/RadiusPacket.cpp RadiusClass/RadiusConfig.cpp RadiusClass/RadiusServer.cpp RadiusClass/RadiusVendorSpecificAttribute.cpp -lgcrypt diff --git a/README.md b/README.md new file mode 100755 index 0000000..4ca4c2d --- /dev/null +++ b/README.md @@ -0,0 +1,177 @@ +# radiusplugin - An OpenVPN plugin for do RADIUS authentication and accounting + +# Documentation +The documentation is done with doxygen. +To generate the documentation execute `doxygen doxygen.conf` in the folder of the plugin. + +# Installation +1. Requirements: the `libgcrypt` library. +2. Compile: see section [Compile](#compile) +3. Update OpenVPN server config: add `plugin /path/to/plugin/radiusplugin.so` as a single line at to the top of the file +4. Create a configfile for the plugin, see [radiusplugin.cnf](https://github.com/wannabehero/openvpn-radiusplugin/blob/b0ca05cd58d5d8157443634d44185b11088312eb/radiusplugin.cnf). +If no path is set in the configfile of OpenVPN the plugin will read the file `/etc/openvpn/radiusplugin.cnf` +The configuration is case-sensitive. +5. Update some configuration in the config file of the OpenVPN server: +- `status /var/log/openvpn/status.log 1` - The status, where the plugin reads the accounting information from.` +6. Update the config file of the OpenVPN client: +- `auth-user-pass` Set this for sending a username and password to the server (either promted or taken for the env) +7. If you use `auth_control_file` (>= OpenVPN 2.1 rc8), the plugin directory needs write permission to the OpenVPN directory or use the `--tmp-dir` option to define a directory for the files. +8. Maybe write your own script for vendor specific attributes (see section [Vendor specific attributes](#vendor-specific-attributes)). + + +## FREERADIUS example +``` +user1 Cleartext-Password := "testing" + Service-Type = Framed-User, + Framed-IP-Netmask = 255.255.255.0, + Framed-IP-Address = 10.8.0.33, + Framed-Routing = Broadcast-Listen, + Framed-Compression = Van-Jacobsen-TCP-IP, + Framed-Route += "192.168.101.0/26 10.8.0.1/32 1", + Framed-Route += "192.168.111.0/24 10.8.0.1/32 1", + Framed-Route += "192.168.112.0/24 10.8.0.1/32 1", + Acct-Interim-Interval=5, + Ascend-Data-Rate=100, + Ascend-Xmit-Rate=200, + Framed-Protocol = PPP +``` + +The Framed-Route attribute must be in the format as shown above. + + +## Compile +```sh +$ make +``` + +*(tested with Debian on x86, for other architectures see section TROUBLESHOOTING)* + + +## RADIUS packets and OpenVPN events +*see [OpenVPN Guide](https://openvpn.net/community-resources/management-interface/) for scripting and environmental variables* + +OpenVPN event |RADIUS PACKET |Information +------------- |------------ |------------ +`auth-user-pass-verify` |ACCESS REQUEST |parsed attributes (see below) +`client-connect` |ACCOUNTING REQUEST (Start) | +\- |ACCOUNTING REQUEST (Update) |If the user had a acct interim interval attribute the update is sent periodically with this interval +`client-disconnect` |ACCOUNTING REQUEST (Stop) |\- + +### RADIUS attributes which are beng parsed by the plugin +- Framed ip address +- Acct Interim Interval +- Framed Routes (only in following format, e.g.: "192.168.101.0/26 10.8.0.1/32 1") + +## Other radius attributes +Reply Message → the text is forwarded to `stderr` + +## Vendor specific attributes +If you want use vendor specific attributes the plugin can call your own program or script on the OpenVPN action `CLIENT-CONNECT` and `CLIENT-DISCONNECT`. +If you want to use this feature you have to specify the program and a named pipe for communication in the config file. +[vsascript.pl](https://github.com/wannabehero/openvpn-radiusplugin/blob/181bf81b86ddb4d256f66827e6f198483c6660fa/vsascript.pl) shows an example. + +## Limits +commonname of the certificate 256 characters +routes entry from the radius server 50 characters + +### Limits in RadiusConfig for the configfile: +(The limit is the length of the array minus 1!) +``` +char serviceType[2]; +char framedProtocol[2]; +char nasPortType[2]; +char nasIdentifier[128]; +char nasIpAddress[16]; +char p2p[16]; +char subnet[16]; +``` + + +## Troubleshooting +- BSD: + - RADIUS attributes FramedIP and FramedRoutes mandatory + - RADIUS attribute FramedIP is ignored + - Use gmake -f Makefile.bsd (maybe you have to adapt library and include path, it was tested for FreeBSD) + +- Setting of Framed-Routes fails under BSD: + - At the moment the plugin can only set the routes on Linux OSs. + - The system call for "route ... " must be changed. Please contact me for a patch. + - Newer version will integrate OS dependent calls. + +- OpenVPN hangs when a client connects: + - Check OpenVPN config and check the OpenVPN log (debuglevel 7): + - The plugin needs following varibales from OpenVPN in ENVP-array: + - username, password, untrusted_ip, common_name, untrusted_port, ifconfig_pool_remote_ip, verb + - In some cases ifconfig_pool_remote_ip is missing (Depends on OpenVPN config). + +- The radius packets are sent over the wrong interface: + - Sometimes on multi interface machines with IPSec the operating system doesn't find the right interface. + - Use the plugin from Magne J. Andreassen (it is for version 1.2a) + +- The plugin writes client config files without any entry: + - The plugin always writes the file for every connecting client, please set the ccd path + in the config file of the plugin to a existing dummy directory. + +- Compiling fails on Solaris 10: + - add following options: -lsocket -lnsl -lresolv + +- Values for BytesIn/BytesOut are 0 in the accounting log of the RADIUS server: + - Make sure that the status file of OpenVPN exists and the plugin can read it. + - Use version 1 for the status file. + +- Error at compiling: not initialised shared memory before the call to the MD5 function: + - Make sure that you use libgrypt 1.2.0 or higher. + +- Error at compiling: DBG: md_enable: algorithm -4195948 not available: + - Make sure that you use libgrypt 1.2.0 or higher. + +- The plugin can't write the auth_control_file. + - The plugin needs write permission in the OpenVPN directory. + + +## Testing +**Attention:** Some values are hard coded in `main.cpp` testing, so you can get unexpected values when testing. +For example the `framedip` is always `10.8.0.100`, because it is read from the environment variable `ifconfig_pool_remote_ip`, which is passed to the plugin from OpenVPN! + +All hard coded variabled: +``` +env1[0]="username=user1"; +env1[1]="password=testing"; +env1[2]="verb=10"; +env1[3]="untrusted_ip=127.0.0.1"; +env1[4]="common_name=R-VPNGateway1"; +env1[5]="trusted_ip=127.0.0.1"; +env1[6]="ifconfig_pool_remote_ip=10.8.0.100"; +``` + +compile for test with a main function: +```sh +g++ -Wall -o main AccountingProcess.cpp Exception.cpp PluginContext.cpp UserAuth.cpp AcctScheduler.cpp IpcSocket.cpp radiusplugin.cpp User.cpp AuthenticationProcess.cpp main.cpp UserAcct.cpp UserPlugin.cpp Config.cpp RadiusClass/RadiusAttribute.cpp RadiusClass/RadiusPacket.cpp RadiusClass/RadiusConfig.cpp RadiusClass/RadiusServer.cpp RadiusClass/RadiusVendorSpecificAttribute.cpp -lgcrypt +``` + +# License +Copyright (C) 2005 EWE TEL GmbH/Ralf Luebben + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Note from the author +The plugin was developed during my diploma thesis for the EWETEL GmbH (www.ewetel.net). +They main idea was to create a virtualized SSL-VPN with RADIUS support. +The virtualization was done by XEN. +At this place I would like to thank the people from EWETEL GmbH and EWE AG (www.ewe.de) +who supported me with the required equipment, hardware +and helped me with all my questions. + +The plugin was developed and tested under Debian Sarge and OpenVPN 2.0 on x86-machines. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..12b22cf --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ +# TODO +- [ ] integrate system dependent code for system call route ... +- [ ] debug - class; +- [ ] create a method which returns a representation of the buffer dependent on the type (enum, ipaddr, string, integer) +- [ ] get the NAS ip address automatically from the system + - help: find the right interface which knows the route to the radius server! + - or create a /32-interface which is routed in the network, so it is always the same +- [ ] improve the parsing of framed routes and netmask +- [ ] set routes over a socket and not over system (route add -net ...) +- [ ] set timeouts for the ipc socket send and receive methods +- [ ] exceptionhandling with try and catch +- [ ] create a routing and ip class with correctness checking +- [ ] read config options from OpenVPN: + - topology option + - get to know if no certificates are used + - check if preconditions are ok +- [ ] different status file version +- [ ] OpenBSD compatible +- [ ] send commonname to RADIUS server +- [ ] vsascript call also at authentication diff --git a/ToDo b/ToDo deleted file mode 100644 index 947d0cc..0000000 --- a/ToDo +++ /dev/null @@ -1,21 +0,0 @@ -TODO (general): ----------------- -- integrate system dependent code for system call route ... -- debug - class; -- create a method which returns a representation of the buffer dependent on the type (enum, ipaddr, string, integer) -- get the NAS ip address automatically from the system - -> help: find the right interface which knows the route to the radius server! - -> or create a /32-interface which is routed in the network, so it is always the same -- improve the parsing of framed routes and netmask -- set routes over a socket and not over system (route add -net ...) -- set timeouts for the ipc socket send and receive methods -- exceptionhandling with try and catch -- create a routing and ip class with correctness checking -- read config options from OpenVPN: - - topology option - - get to know if no certificates are used - - check if preconditions are ok -- different status file version -- OpenBSD compatible -- send commonname to RADIUS server -- vsascript call also at authentication \ No newline at end of file diff --git a/UserAcct.cpp b/UserAcct.cpp index d0ca96b..73765c3 100755 --- a/UserAcct.cpp +++ b/UserAcct.cpp @@ -1,7 +1,7 @@ /* - * radiusplugin -- An OpenVPN plugin for do radius authentication + * radiusplugin -- An OpenVPN plugin for do radius authentication * and accounting. - * + * * Copyright (C) 2005 EWE TEL GmbH/Ralf Luebben * * This program is free software; you can redistribute it and/or modify @@ -18,7 +18,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - + #include "UserAcct.h" #include "radiusplugin.h" @@ -43,7 +43,7 @@ UserAcct::~UserAcct() * @param u A refernece to a UserAcct.*/ UserAcct & UserAcct::operator=(const UserAcct &u) { - + if (this!=&u) { this->User::operator=(u); @@ -71,13 +71,13 @@ UserAcct::UserAcct(const UserAcct &u):User(u) this->bytesout=u.bytesout; this->nextupdate=u.nextupdate; this->starttime=u.starttime; - + } /** The method sends an accounting update packet for the user to the radius server. * The accounting information are read from the OpenVpn * status file. The following attributes are sent to the radius server: - * - User_Name, + * - User_Name, * - Framed_IP_Address, * - NAS_Port, * - Calling_Station_Id, @@ -97,10 +97,14 @@ UserAcct::UserAcct(const UserAcct &u):User(u) * @return An integer, 0 is everything is ok, else 1.*/ int UserAcct::sendUpdatePacket(PluginContext *context) { - + if (context->conf.getAccountingEnabled() == false) { + cerr << getTime() << "RADIUS-PLUGIN: Skip accounting user::sendUpdatePacket\n"; + return 0; + } + list * serverlist; list::iterator server; - + RadiusPacket packet(ACCOUNTING_REQUEST); RadiusAttribute ra1(ATTRIB_User_Name,this->getUsername()), ra2(ATTRIB_Framed_IP_Address,this->getFramedIp()), @@ -117,37 +121,37 @@ int UserAcct::sendUpdatePacket(PluginContext *context) ra13(ATTRIB_Acct_Output_Octets, this->bytesout), ra14(ATTRIB_Acct_Session_Time), ra15(ATTRIB_Acct_Input_Gigawords, this->gigain), - ra16(ATTRIB_Acct_Output_Gigawords, this->gigaout); - - - + ra16(ATTRIB_Acct_Output_Gigawords, this->gigaout); + + + //get the server list serverlist=context->radiusconf.getRadiusServer(); - + //set server on the first server server=serverlist->begin(); - - //add the attributes to the radius packet + + //add the attributes to the radius packet if(packet.addRadiusAttribute(&ra1)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_User_Name.\n"; } - + if (packet.addRadiusAttribute(&ra2)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_User_Password.\n"; } - + if (packet.addRadiusAttribute(&ra3)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Port.\n"; } - + if (packet.addRadiusAttribute(&ra4)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Calling_Station_Id.\n"; } - + //get the values from the config and add them to the packet if(strcmp(context->radiusconf.getNASIdentifier(),"")) { @@ -157,7 +161,7 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Identifier.\n"; } } - + if(strcmp(context->radiusconf.getNASIpAddress(),"")) { if(ra6.setValue(context->radiusconf.getNASIpAddress())!=0) @@ -169,7 +173,7 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Ip_Address.\n"; } } - + if(strcmp(context->radiusconf.getNASPortType(),"")) { ra7.setValue(context->radiusconf.getNASPortType()); @@ -178,7 +182,7 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Port_Type.\n"; } } - + if(strcmp(context->radiusconf.getServiceType(),"")) { ra8.setValue(context->radiusconf.getServiceType()); @@ -187,17 +191,17 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Service_Type.\n"; } } - + if (packet.addRadiusAttribute(&ra9)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Session_ID.\n"; } - + if (packet.addRadiusAttribute(&ra10)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Session_ID.\n"; } - + if(strcmp(context->radiusconf.getFramedProtocol(),"")) { ra11.setValue(context->radiusconf.getFramedProtocol()); @@ -206,12 +210,12 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Framed_Protocol.\n"; } } - + if (packet.addRadiusAttribute(&ra12)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Input_Packets.\n"; } - + if (packet.addRadiusAttribute(&ra13)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Output_Packets.\n"; @@ -229,13 +233,13 @@ int UserAcct::sendUpdatePacket(PluginContext *context) if (packet.addRadiusAttribute(&ra16)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Output_Gigawords.\n"; } - + //send the packet to the server if (packet.radiusSend(server)<0) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Packet was not sent.\n"; } - + //get the response if (packet.radiusReceive(serverlist)>=0) { @@ -245,9 +249,9 @@ int UserAcct::sendUpdatePacket(PluginContext *context) if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Get ACCOUNTING_RESPONSE-Packet.\n"; - + return 0; - + } else { @@ -255,15 +259,15 @@ int UserAcct::sendUpdatePacket(PluginContext *context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: No response on accounting request.\n"; return 1; } - - + + } return 1; } /** The method sends an accounting start packet for the user to the radius server. * The following attributes are sent to the radius server: - * - User_Name, + * - User_Name, * - Framed_IP_Address, * - NAS_Port, * - Calling_Station_Id, @@ -278,6 +282,11 @@ int UserAcct::sendUpdatePacket(PluginContext *context) * @return An integer, 0 is everything is ok, else 1.*/ int UserAcct::sendStartPacket(PluginContext * context) { + if (context->conf.getAccountingEnabled() == false) { + cerr << getTime() << "RADIUS-PLUGIN: Skip accounting user::sendStartPacket\n"; + return 0; + } + list* serverlist; list::iterator server; RadiusPacket packet(ACCOUNTING_REQUEST); @@ -292,21 +301,21 @@ int UserAcct::sendStartPacket(PluginContext * context) ra9(ATTRIB_Acct_Session_ID, this->getSessionId()), ra10(ATTRIB_Acct_Status_Type,string("1")), // "Start" ra11(ATTRIB_Framed_Protocol); - - - + + + //get the radius server from the config serverlist=context->radiusconf.getRadiusServer(); - + //set server to the first from the list server=serverlist->begin(); - + //add the attributes to the packet if(packet.addRadiusAttribute(&ra1)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_User_Name.\n"; } - + if (packet.addRadiusAttribute(&ra2)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_User_Password.\n"; @@ -319,7 +328,7 @@ int UserAcct::sendStartPacket(PluginContext * context) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Calling_Station_Id.\n"; } - + //get information from the config and add the attributes to the packet if(strcmp(context->radiusconf.getNASIdentifier(),"")) { @@ -329,14 +338,14 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Identifier.\n"; } } - + if(strcmp(context->radiusconf.getNASIpAddress(),"")) { if(ra6.setValue(context->radiusconf.getNASIpAddress())!=0) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to set value ATTRIB_NAS_Ip_Address.\n"; } - + if (packet.addRadiusAttribute(&ra6)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Ip_Address.\n"; @@ -350,7 +359,7 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Port_Type.\n"; } } - + if(strcmp(context->radiusconf.getServiceType(),"")) { ra8.setValue(context->radiusconf.getServiceType()); @@ -359,15 +368,15 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Service_Type.\n"; } } - + if (packet.addRadiusAttribute(&ra9)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Session_ID.\n"; } - + if (packet.addRadiusAttribute(&ra10)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Session_ID.\n"; } - + if(strcmp(context->radiusconf.getFramedProtocol(),"")) { ra11.setValue(context->radiusconf.getFramedProtocol()); @@ -376,13 +385,13 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Framed_Protocol.\n"; } } - - //send the packet + + //send the packet if (packet.radiusSend(server)<0) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Packet was not sent.\n"; } - + //receive the response int ret=packet.radiusReceive(serverlist); if (ret>=0) @@ -394,7 +403,7 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Get ACCOUNTING_RESPONSE-Packet.\n"; return 0; - + } else { @@ -402,13 +411,13 @@ int UserAcct::sendStartPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Get no ACCOUNTING_RESPONSE-Packet.\n"; return 1; } - + } else { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Error on receiving radius response, code: " << ret << endl; } - + return 1; } @@ -417,7 +426,7 @@ int UserAcct::sendStartPacket(PluginContext * context) /** The method sends an accounting stop packet for the user to the radius server. * The accounting information are read from the OpenVpn * status file. The following attributes are sent to the radius server: - * - User_Name, + * - User_Name, * - Framed_IP_Address, * - NAS_Port, * - Calling_Station_Id, @@ -435,6 +444,11 @@ int UserAcct::sendStartPacket(PluginContext * context) * @return An integer, 0 is everything is ok, else 1.*/ int UserAcct::sendStopPacket(PluginContext * context) { + if (context->conf.getAccountingEnabled() == false) { + cerr << getTime() << "RADIUS-PLUGIN: Skip accounting user::sendStopPacket\n"; + return 0; + } + list * serverlist; list::iterator server; RadiusPacket packet(ACCOUNTING_REQUEST); @@ -453,22 +467,22 @@ int UserAcct::sendStopPacket(PluginContext * context) ra13(ATTRIB_Acct_Output_Octets, this->bytesout), ra14(ATTRIB_Acct_Session_Time), ra15(ATTRIB_Acct_Input_Gigawords, this->gigain), - ra16(ATTRIB_Acct_Output_Gigawords, this->gigaout); - - - + ra16(ATTRIB_Acct_Output_Gigawords, this->gigaout); + + + //get the server from the config serverlist=context->radiusconf.getRadiusServer(); - + //set server to the first server server=serverlist->begin(); - + //add the attributes to the packet if(packet.addRadiusAttribute(&ra1)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_User_Name.\n"; } - + if (packet.addRadiusAttribute(&ra2)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_FramedIP_Address.\n"; @@ -481,7 +495,7 @@ int UserAcct::sendStopPacket(PluginContext * context) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Calling_Station_Id.\n"; } - + //get information from th config and ad it to the packet if(strcmp(context->radiusconf.getNASIdentifier(),"")) { @@ -491,7 +505,7 @@ int UserAcct::sendStopPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Identifier.\n"; } } - + if(strcmp(context->radiusconf.getNASIpAddress(),"")) { if(ra6.setValue(context->radiusconf.getNASIpAddress())!=0) @@ -512,7 +526,7 @@ int UserAcct::sendStopPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_NAS_Port_Type.\n"; } } - + if(strcmp(context->radiusconf.getServiceType(),"")) { ra8.setValue(context->radiusconf.getServiceType()); @@ -529,7 +543,7 @@ int UserAcct::sendStopPacket(PluginContext * context) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Session_ID.\n"; } - + if(strcmp(context->radiusconf.getFramedProtocol(),"")) { ra11.setValue(context->radiusconf.getFramedProtocol()); @@ -538,9 +552,9 @@ int UserAcct::sendStopPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Framed_Protocol.\n"; } } - - - + + + if (packet.addRadiusAttribute(&ra12)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Input_Packets.\n"; @@ -549,7 +563,7 @@ int UserAcct::sendStopPacket(PluginContext * context) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Output_Packets.\n"; } - + //calculate the session time ra14.setValue(time(NULL)-this->starttime); if (packet.addRadiusAttribute(&ra14)) { @@ -563,13 +577,13 @@ int UserAcct::sendStopPacket(PluginContext * context) if (packet.addRadiusAttribute(&ra16)) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Fail to add attribute ATTRIB_Acct_Output_Gigawords.\n"; } - + //send the packet if (packet.radiusSend(server)<0) { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Packet was not sent.\n"; } - + //get the response if (packet.radiusReceive(serverlist)>=0) { @@ -580,7 +594,7 @@ int UserAcct::sendStopPacket(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Get ACCOUNTING_RESPONSE-Packet.\n"; return 0; - + } else { @@ -589,7 +603,7 @@ int UserAcct::sendStopPacket(PluginContext * context) return 1; } } - + return 1; } @@ -600,14 +614,14 @@ void UserAcct::delSystemRoutes(PluginContext * context) { char * route; char framedip[40]; - + char routestring[200]; - char framednetmask_cidr[4]; + char framednetmask_cidr[4]; char framedgw[40]; - char framedmetric[5]; + char framedmetric[5]; char * framedroutes, * framedroutes6; int j=0,k=0,len=0; - + //copy the framed route string to an char array, it is easier to //analyse try{ @@ -618,10 +632,10 @@ void UserAcct::delSystemRoutes(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for framedroutes." << endl; } memset(framedroutes,0,this->getFramedRoutes().size()+1); - + // copy in a temp-string, because strtok deletes the delimiter, if it used anywhere strncpy(framedroutes,this->getFramedRoutes().c_str(),this->getFramedRoutes().size()); - + //are there framed routes if (framedroutes[0]!='\0') { @@ -635,14 +649,14 @@ void UserAcct::delSystemRoutes(PluginContext * context) else { while (route!=NULL) - { + { //set the arrays to 0 memset(routestring,0,100); memset(framednetmask_cidr,0,3); memset(framedip,0,16); memset(framedgw,0,16); memset(framedmetric,0,5); - + j=0;k=0; //get ip address and add it to framedip while(route[j]!='/' && j /dev/null",13); - - + + if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Create route string "<< routestring <<".\n"; - + //system call - if(system(routestring)!=0) + if(system(routestring)!=0) //if(1)//-> the debugg can't context system() { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Route " << routestring << " could not set. Route already set or bad route string.\n"; @@ -739,13 +753,13 @@ void UserAcct::delSystemRoutes(PluginContext * context) { if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Add route to system routing table.\n"; - + } //get the next route - route=strtok(NULL,";"); + route=strtok(NULL,";"); } } - + } else { @@ -754,16 +768,16 @@ void UserAcct::delSystemRoutes(PluginContext * context) } //free the char array delete [] framedroutes; - + //copy the framed route string to an char array, it is easier to //analyse framedroutes6=new char[this->getFramedRoutes6().size()+1]; memset(framedroutes6,0,this->getFramedRoutes6().size()+1); - + // copy in a temp-string, because strtok deletes the delimiter, if it used anywhere strncpy(framedroutes6,this->getFramedRoutes6().c_str(),this->getFramedRoutes6().size()); - + //are there framed routes if (framedroutes6[0]!='\0') { @@ -777,14 +791,14 @@ void UserAcct::delSystemRoutes(PluginContext * context) else { while (route!=NULL) - { + { //set the arrays to 0 memset(routestring,0,200); memset(framednetmask_cidr,0,4); memset(framedip,0,40); memset(framedgw,0,40); memset(framedmetric,0,5); - + j=0;k=0; //get ip address and add it to framedip while(route[j]!='/' && j /dev/null",13); - - + + if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Create IPv6 route string "<< routestring <<".\n"; - + //system call - if(system(routestring)!=0) + if(system(routestring)!=0) //if(1)//-> the debugg can't context system() { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Route " << routestring << " could not set. Route already set or bad route string.\n"; @@ -881,13 +895,13 @@ void UserAcct::delSystemRoutes(PluginContext * context) { if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Add route to system routing table.\n"; - + } //get the next route - route=strtok(NULL,";"); + route=strtok(NULL,";"); } } - + } else { @@ -896,7 +910,7 @@ void UserAcct::delSystemRoutes(PluginContext * context) } //free the char array delete [] framedroutes6; - + } /** The method adds ths routes of the user to the system routing table. @@ -906,14 +920,14 @@ void UserAcct::addSystemRoutes(PluginContext * context) { char * route; char framedip[40]; - + char routestring[200]; - char framednetmask_cidr[4]; + char framednetmask_cidr[4]; char framedgw[40]; - char framedmetric[5]; + char framedmetric[5]; char * framedroutes, * framedroutes6; int j=0,k=0,len=0; - + //copy the framed route string to an char array, it is easier to //analyse try{ @@ -924,10 +938,10 @@ void UserAcct::addSystemRoutes(PluginContext * context) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND ACCT: New failed for framedroutes." << endl; } memset(framedroutes,0,this->getFramedRoutes().size()+1); - + // copy in a temp-string, becaue strtok deletes the delimiter, if it used anywhere strncpy(framedroutes,this->getFramedRoutes().c_str(),this->getFramedRoutes().size()); - + //are there framed routes if (framedroutes[0]!='\0') { @@ -941,14 +955,14 @@ void UserAcct::addSystemRoutes(PluginContext * context) else { while (route!=NULL) - { + { //set the arrays to 0 memset(routestring,0,100); memset(framednetmask_cidr,0,3); memset(framedip,0,16); memset(framedgw,0,16); memset(framedmetric,0,5); - + j=0;k=0; //get ip address and add it to framedip while(route[j]!='/' && j /dev/null",13); - - + + if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Create route string "<< routestring << ".\n"; - + //system call route - if(system(routestring)!=0) + if(system(routestring)!=0) //if(1)//-> the debugg can't context system() { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Route " << routestring << " could not set. Route already set or bad route string.\n"; @@ -1046,10 +1060,10 @@ void UserAcct::addSystemRoutes(PluginContext * context) { if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Add route to system routing table.\n"; - + } //get the next route - route=strtok(NULL,";"); + route=strtok(NULL,";"); } } } @@ -1060,15 +1074,15 @@ void UserAcct::addSystemRoutes(PluginContext * context) } //free the char array delete [] framedroutes; - + //copy the framed route string to an char array, it is easier to //analyse framedroutes6=new char[this->getFramedRoutes6().size()+1]; memset(framedroutes6,0,this->getFramedRoutes6().size()+1); - + // copy in a temp-string, becaue strtok deletes the delimiter, if it used anywhere strncpy(framedroutes6,this->getFramedRoutes6().c_str(),this->getFramedRoutes6().size()); - + //are there framed routes if (framedroutes6[0]!='\0') { @@ -1082,14 +1096,14 @@ void UserAcct::addSystemRoutes(PluginContext * context) else { while (route!=NULL) - { + { //set the arrays to 0 memset(routestring,0,200); memset(framednetmask_cidr,0,4); memset(framedip,0,40); memset(framedgw,0,40); memset(framedmetric,0,5); - + j=0;k=0; //get ip address and add it to framedip while(route[j]!='/' && j /dev/null",13); - - + + if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Create IPv6 route string "<< routestring << " dev " << this->getDev() << ".\n"; - + //system call route - if(system(routestring)!=0) + if(system(routestring)!=0) //if(1)//-> the debugg can't context system() { cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Route " << routestring << " could not set. Route already set or bad route string.\n"; @@ -1187,10 +1201,10 @@ void UserAcct::addSystemRoutes(PluginContext * context) { if (DEBUG (context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: BACKGROUND-ACCT: Add route to system routing table.\n"; - + } //get the next route - route=strtok(NULL,";"); + route=strtok(NULL,";"); } } } @@ -1201,7 +1215,7 @@ void UserAcct::addSystemRoutes(PluginContext * context) } //free the char array delete [] framedroutes6; - + } diff --git a/radiusplugin.cnf b/radiusplugin.cnf index c7ff9a0..f71f72e 100755 --- a/radiusplugin.cnf +++ b/radiusplugin.cnf @@ -23,9 +23,9 @@ OpenVPNConfig=/etc/openvpn/server.conf # Support for topology option in OpenVPN 2.1 -# If you don't specify anything, option "net30" (default in OpenVPN) is used. +# If you don't specify anything, option "net30" (default in OpenVPN) is used. # You can only use one of the options at the same time. -# If you use topology option "subnet", fill in the right netmask, e.g. from OpenVPN option "--server NETWORK NETMASK" +# If you use topology option "subnet", fill in the right netmask, e.g. from OpenVPN option "--server NETWORK NETMASK" subnet=255.255.255.0 # If you use topology option "p2p", fill in the right network, e.g. from OpenVPN option "--server NETWORK NETMASK" # p2p=10.8.0.1 @@ -59,7 +59,12 @@ useclientconnectdeferfile=true # default is false # accountingonly=false -# If the accounting is non essential, nonfatalaccounting can be set to true. +# Allows to disable accounting +# Some RADIUS servers apparently do not support accounting packets which in turn breakes the auth process +# default is true +# accountingenabled=true + +# If the accounting is non essential, nonfatalaccounting can be set to true. # If set to true all errors during the accounting procedure are ignored, which can be # - radius accounting can fail # - FramedRouted (if configured) maybe not configured correctly diff --git a/radiusplugin.cpp b/radiusplugin.cpp index 2af2e94..6892fe0 100755 --- a/radiusplugin.cpp +++ b/radiusplugin.cpp @@ -46,173 +46,158 @@ extern "C" * @param The list of environmental variables, it is created by the OpenVpn-Process. */ - //OPENVPN_EXPORT openvpn_plugin_handle_t //openvpn_plugin_open_v1 ( unsigned int *type_mask, const char *argv[], const char *envp[] ) OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v2)(unsigned int *type_mask, - const char *argv[], - const char *envp[], - struct openvpn_plugin_string_list **return_list) + const char *argv[], + const char *envp[], + struct openvpn_plugin_string_list **return_list) { - pid_t pid; /**setVerbosity ( atoi ( verb_string ) ); + if (verb_string) + context->setVerbosity(atoi(verb_string)); - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start AUTH-RADIUS-PLUGIN\n"; - // Make sure we have one string argument: the .so name. - if ( string_array_len ( argv ) < base_parms ) + if (string_array_len(argv) < base_parms) { cerr << getTime() << "RADIUS-PLUGIN: no .so name\n"; goto error; } - - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: Found "<< string_array_len ( argv ) << " params.\n"; - + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: Found " << string_array_len(argv) << " params.\n"; // See if we have optional name/value pairs for // the plugin, this can only be the config file. // path (default: -c /etc/openvpn/radiusplugin.conf) name_value_list.len = 0; - if ( string_array_len ( argv ) > base_parms ) + if (string_array_len(argv) > base_parms) { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Find params.\n"; //just a work around because argv[1] is the filename name_value_list.data[0].name = "-c"; name_value_list.data[0].value = argv[1]; - if ( strncmp ( name_value_list.data[0].name,"-c",2 ) ==0 ) + if (strncmp(name_value_list.data[0].name, "-c", 2) == 0) { //see in ./RadiusClass/error.h for errornumbers //parse the radiusplugin config file - cerr << getTime() << "RADIUS-PLUGIN: Configfile name: "<< name_value_list.data[0].value << ".\n"; - if ( context->radiusconf.parseConfigFile ( name_value_list.data[0].value ) !=0 or context->conf.parseConfigFile ( name_value_list.data[0].value ) !=0 ) + cerr << getTime() << "RADIUS-PLUGIN: Configfile name: " << name_value_list.data[0].value << ".\n"; + if (context->radiusconf.parseConfigFile(name_value_list.data[0].value) != 0 or context->conf.parseConfigFile(name_value_list.data[0].value) != 0) { cerr << getTime() << "RADIUS-PLUGIN: Bad config file or error in config.\n"; goto error; } - - } else { cerr << getTime() << "RADIUS-PLUGIN: Bad argument for plugin.\n"; goto error; } - } else { //if there is no filename, use the default //parse the radiusplugin config file cerr << getTime() << "RADIUS-PLUGIN: Configfile name: /etc/openvpn/radiusplugin.cnf.\n"; - if ( context->radiusconf.parseConfigFile ( "/etc/openvpn/radiusplugin.cnf" ) !=0 or context->conf.parseConfigFile ( "/etc/openvpn/radiusplugin.cnf" ) !=0 ) + if (context->radiusconf.parseConfigFile("/etc/openvpn/radiusplugin.cnf") != 0 or context->conf.parseConfigFile("/etc/openvpn/radiusplugin.cnf") != 0) { cerr << getTime() << "RADIUS-PLUGIN: Bad config file or error in config.\n"; goto error; } - } - // Intercept the --auth-user-pass-verify, --client-connect and --client-disconnect callback. - if (context->conf.getAccountingOnly()==false) + if (context->conf.getAccountingOnly() == false) { - *type_mask = OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY ) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_CONNECT ) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_DISCONNECT ); + *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT) | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT); } else //just do the accounting { - *type_mask = OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_CONNECT ) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_DISCONNECT ); + *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT) | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT); } // Make a socket for foreground and background processes // to communicate. //Authentication process: - if ( socketpair ( PF_UNIX, SOCK_DGRAM, 0, fd_auth ) == -1 ) + if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd_auth) == -1) { cerr << getTime() << "RADIUS-PLUGIN: socketpair call failed for authentication process\n"; goto error; } //Accounting process: - if ( socketpair ( PF_UNIX, SOCK_DGRAM, 0, fd_acct ) == -1 ) + if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd_acct) == -1) { cerr << getTime() << "RADIUS-PLUGIN: socketpair call failed for accounting process\n"; goto error; } - - - // Fork off the privileged processes. It will remain privileged // even after the foreground process drops its privileges. - // Fork the authentication process - pid = fork (); - if ( pid ) + pid = fork(); + if (pid) { // Foreground Process (Parent) int status; //save the process id - context->setAuthPid ( pid ); + context->setAuthPid(pid); // close our copy of child's socket - close ( fd_auth[1] ); + close(fd_auth[1]); /* don't let future subprocesses inherit child socket */ - if ( fcntl ( fd_auth[0], F_SETFD, FD_CLOEXEC ) < 0 ) + if (fcntl(fd_auth[0], F_SETFD, FD_CLOEXEC) < 0) cerr << getTime() << "RADIUS-PLUGIN: Set FD_CLOEXEC flag on socket file descriptor failed\n"; - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start BACKGROUND Process for authentication with PID " << context->getAuthPid() << ".\n"; //save the socket number in the context - context->authsocketbackgr.setSocket ( fd_auth[0] ); + context->authsocketbackgr.setSocket(fd_auth[0]); //wait for background child process to initialize */ status = context->authsocketbackgr.recvInt(); - if ( status != RESPONSE_INIT_SUCCEEDED ) + if (status != RESPONSE_INIT_SUCCEEDED) { //set the socket to -1 if the initialization failed - context->authsocketbackgr.setSocket ( -1 ); + context->authsocketbackgr.setSocket(-1); } - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start AUTH-RADIUS-PLUGIN\n"; } else @@ -221,60 +206,60 @@ extern "C" //Background Process // close all parent fds except our socket back to parent - close_fds_except ( fd_auth[1] ); + close_fds_except(fd_auth[1]); // Ignore most signals (the parent will receive them) - set_signals (); + set_signals(); //save the socket number in the context - context->authsocketforegr.setSocket ( fd_auth[1] ); + context->authsocketforegr.setSocket(fd_auth[1]); //start the backgroung event loop for accounting - Auth.Authentication ( context ); + Auth.Authentication(context); //close the socket - close ( fd_auth[1] ); + close(fd_auth[1]); //free the context of the background process delete context; - exit ( 0 ); + exit(0); return 0; // NOTREACHED } // Fork the accounting process - pid = fork (); - if ( pid ) + pid = fork(); + if (pid) { // Foreground Process (Parent) - int status; //status if the background process + int status; //status if the background process //save the pid - context->setAcctPid ( pid ); + context->setAcctPid(pid); - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start BACKGROUND Process for accounting with PID " << context->getAcctPid() << ".\n"; // close our copy of child's socket */ - close ( fd_acct[1] ); + close(fd_acct[1]); /* don't let future subprocesses inherit child socket */ - if ( fcntl ( fd_acct[0], F_SETFD, FD_CLOEXEC ) < 0 ) + if (fcntl(fd_acct[0], F_SETFD, FD_CLOEXEC) < 0) cerr << getTime() << "RADIUS-PLUGIN: Set FD_CLOEXEC flag on socket file descriptor failed\n"; //save the socket number in the context - context->acctsocketbackgr.setSocket ( fd_acct[0] ); + context->acctsocketbackgr.setSocket(fd_acct[0]); // wait for background child process to initialize */ status = context->acctsocketbackgr.recvInt(); - if ( status != RESPONSE_INIT_SUCCEEDED ) + if (status != RESPONSE_INIT_SUCCEEDED) { //set the socket to -1 if the initialization failed - context->acctsocketbackgr.setSocket ( -1 ); + context->acctsocketbackgr.setSocket(-1); } - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start AUTH-RADIUS-PLUGIN\n"; } else @@ -283,26 +268,26 @@ extern "C" //Background Process // close all parent fds except our socket back to parent - close_fds_except ( fd_acct[1] ); + close_fds_except(fd_acct[1]); // Ignore most signals (the parent will receive them) - set_signals (); + set_signals(); - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: Start BACKGROUND Process for accounting\n"; // save the socket in the context - context->acctsocketforegr.setSocket ( fd_acct[1] ); + context->acctsocketforegr.setSocket(fd_acct[1]); //start the backgroung event loop for accounting - Acct.Accounting ( context ); + Acct.Accounting(context); //close the socket - close ( fd_acct[1] ); + close(fd_acct[1]); //free the context of the background process delete context; - exit ( 0 ); + exit(0); return 0; // NOTREACHED } @@ -310,16 +295,15 @@ extern "C" //openvpn_plugin_open_v1 //openvpn_plugin_func_v1 //openvpn_plugin_close_v1 - return ( openvpn_plugin_handle_t ) context; + return (openvpn_plugin_handle_t)context; -error: + error: //delete the context - if ( context ) - delete ( context ); + if (context) + delete (context); return NULL; } - /** This function is called from the OpenVpn process every time * a event happens. The function handle the events (plugins) * AUTH_USER_PASS_VERIFY, CLIENT_CONNECT, CLIENT_DISCONNECT. @@ -342,38 +326,36 @@ extern "C" //OPENVPN_EXPORT int //openvpn_plugin_func_v1 ( openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[] ) - OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2) - (openvpn_plugin_handle_t handle, - const int type, - const char *argv[], - const char *envp[], - void *per_client_context, - struct openvpn_plugin_string_list **return_list) + OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2)(openvpn_plugin_handle_t handle, + const int type, + const char *argv[], + const char *envp[], + void *per_client_context, + struct openvpn_plugin_string_list **return_list) { - //restore the context which was created at the function openvpn_plugin_open_v1 - PluginContext *context = ( struct PluginContext * ) handle; + PluginContext *context = (struct PluginContext *)handle; if (context->getStartThread()) { - pthread_cond_init (context->getCondSend(), NULL); - pthread_mutex_init (context->getMutexSend(), NULL); - pthread_cond_init (context->getCondRecv(), NULL); - pthread_mutex_init (context->getMutexRecv(), NULL); - pthread_cond_init (context->getAcctCondSend(), NULL); - pthread_mutex_init (context->getAcctMutexSend(), NULL); - pthread_cond_init (context->getAcctCondRecv(), NULL); - pthread_mutex_init (context->getAcctMutexRecv(), NULL); + pthread_cond_init(context->getCondSend(), NULL); + pthread_mutex_init(context->getMutexSend(), NULL); + pthread_cond_init(context->getCondRecv(), NULL); + pthread_mutex_init(context->getMutexRecv(), NULL); + pthread_cond_init(context->getAcctCondSend(), NULL); + pthread_mutex_init(context->getAcctMutexSend(), NULL); + pthread_cond_init(context->getAcctCondRecv(), NULL); + pthread_mutex_init(context->getAcctMutexRecv(), NULL); - if (pthread_create(context->getAcctThread(), NULL, &client_connect, (void *) context) != 0) + if (pthread_create(context->getAcctThread(), NULL, &client_connect, (void *)context) != 0) { cerr << getTime() << "RADIUS-PLUGIN: client_connect thread creation failed.\n"; return OPENVPN_PLUGIN_FUNC_ERROR; //goto error; } - if (context->conf.getAccountingOnly()==false && pthread_create(context->getThread(), NULL, &auth_user_pass_verify, (void *) context) != 0) + if (context->conf.getAccountingOnly() == false && pthread_create(context->getThread(), NULL, &auth_user_pass_verify, (void *)context) != 0) { cerr << getTime() << "RADIUS-PLUGIN: auth_user_pass_verify thread creation failed.\n"; return OPENVPN_PLUGIN_FUNC_ERROR; @@ -384,192 +366,190 @@ extern "C" pthread_mutex_unlock(context->getMutexRecv()); } - UserPlugin *newuser=NULL; /**< A context for an new user.*/ - UserPlugin *tmpuser=NULL; /**< A context for an temporary user.*/ - - string common_name; /**authsocketbackgr.getSocket() ) >= 0 ) + if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && (context->authsocketbackgr.getSocket()) >= 0) { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY is called."<< endl; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY is called." << endl; } - + try { - newuser=new UserPlugin(); - get_user_env(context,type,envp, newuser); - if (newuser->getAuthControlFile().length() > 0 && context->conf.getUseAuthControlFile()) - { - pthread_mutex_lock(context->getMutexSend()); - context->addNewUser(newuser); - pthread_cond_signal( context->getCondSend( )); - pthread_mutex_unlock (context->getMutexSend()); - return OPENVPN_PLUGIN_FUNC_DEFERRED; - } - else - { - pthread_mutex_lock(context->getMutexRecv()); - pthread_mutex_lock(context->getMutexSend()); - context->addNewUser(newuser); - pthread_cond_signal( context->getCondSend( )); - pthread_mutex_unlock (context->getMutexSend()); - - pthread_cond_wait( context->getCondRecv(), context->getMutexRecv()); - pthread_mutex_unlock (context->getMutexRecv()); - - return context->getResult(); - } + newuser = new UserPlugin(); + get_user_env(context, type, envp, newuser); + if (newuser->getAuthControlFile().length() > 0 && context->conf.getUseAuthControlFile()) + { + pthread_mutex_lock(context->getMutexSend()); + context->addNewUser(newuser); + pthread_cond_signal(context->getCondSend()); + pthread_mutex_unlock(context->getMutexSend()); + return OPENVPN_PLUGIN_FUNC_DEFERRED; + } + else + { + pthread_mutex_lock(context->getMutexRecv()); + pthread_mutex_lock(context->getMutexSend()); + context->addNewUser(newuser); + pthread_cond_signal(context->getCondSend()); + pthread_mutex_unlock(context->getMutexSend()); + + pthread_cond_wait(context->getCondRecv(), context->getMutexRecv()); + pthread_mutex_unlock(context->getMutexRecv()); + + return context->getResult(); + } } - catch ( Exception &e ) + catch (Exception &e) { cerr << getTime() << e; } catch (std::bad_alloc) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY" << endl; - } - catch ( ... ) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY" << endl; + } + catch (...) { cerr << getTime() << "Unknown Exception!"; - } return OPENVPN_PLUGIN_FUNC_ERROR; /////////////////////////// CLIENT_CONNECT } - if ( type == OPENVPN_PLUGIN_CLIENT_CONNECT && context->acctsocketbackgr.getSocket() >= 0 ) + if (type == OPENVPN_PLUGIN_CLIENT_CONNECT && context->acctsocketbackgr.getSocket() >= 0) { - - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: OPENVPN_PLUGIN_CLIENT_CONNECT is called.\n"; } try { - tmpuser=new UserPlugin(); - get_user_env(context,type,envp, tmpuser); + tmpuser = new UserPlugin(); + get_user_env(context, type, envp, tmpuser); if (tmpuser->getClientConnectDeferFile().length() > 0 && context->conf.getUseClientConnectDeferFile()) { pthread_mutex_lock(context->getAcctMutexSend()); context->addNewAcctUser(tmpuser); - pthread_cond_signal( context->getAcctCondSend( )); - pthread_mutex_unlock (context->getAcctMutexSend()); - return OPENVPN_PLUGIN_FUNC_DEFERRED; + pthread_cond_signal(context->getAcctCondSend()); + pthread_mutex_unlock(context->getAcctMutexSend()); + return OPENVPN_PLUGIN_FUNC_DEFERRED; } else { pthread_mutex_lock(context->getAcctMutexRecv()); pthread_mutex_lock(context->getAcctMutexSend()); context->addNewAcctUser(tmpuser); - pthread_cond_signal( context->getAcctCondSend( )); - pthread_mutex_unlock (context->getAcctMutexSend()); - pthread_cond_wait( context->getAcctCondRecv(), context->getAcctMutexRecv()); - pthread_mutex_unlock (context->getAcctMutexRecv()); + pthread_cond_signal(context->getAcctCondSend()); + pthread_mutex_unlock(context->getAcctMutexSend()); + pthread_cond_wait(context->getAcctCondRecv(), context->getAcctMutexRecv()); + pthread_mutex_unlock(context->getAcctMutexRecv()); return context->getResult(); } } - catch ( Exception &e ) + catch (Exception &e) { cerr << getTime() << e; } catch (std::bad_alloc) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_CLIENT_CONNECT" << endl; - } - catch ( ... ) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_CLIENT_CONNECT" << endl; + } + catch (...) { cerr << getTime() << "Unknown Exception!"; - } return OPENVPN_PLUGIN_FUNC_ERROR; } ///////////////////////// OPENVPN_PLUGIN_CLIENT_DISCONNECT - if ( type == OPENVPN_PLUGIN_CLIENT_DISCONNECT && context->acctsocketbackgr.getSocket() >= 0 ) + if (type == OPENVPN_PLUGIN_CLIENT_DISCONNECT && context->acctsocketbackgr.getSocket() >= 0) { - - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) { cerr << getTime() << "\n\nRADIUS-PLUGIN: FOREGROUND: OPENVPN_PLUGIN_CLIENT_DISCONNECT is called.\n"; } try { - tmpuser=new UserPlugin(); - get_user_env(context,type,envp, tmpuser); + tmpuser = new UserPlugin(); + get_user_env(context, type, envp, tmpuser); //find the user in the context, he was added at the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY //string key=common_name + string ( "," ) +untrusted_ip+string ( ":" ) + string ( get_env ( "untrusted_port", envp ) ); - newuser=context->findUser(tmpuser->getKey()); - delete(tmpuser); - if ( newuser!=NULL ) + newuser = context->findUser(tmpuser->getKey()); + delete (tmpuser); + if (newuser != NULL) { - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Delete user from accounting: commonname: " << newuser->getKey() << "\n"; - + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Delete user from accounting: commonname: " << newuser->getKey() << "\n"; //send the information to the background process - context->acctsocketbackgr.send ( DEL_USER ); - context->acctsocketbackgr.send ( newuser->getKey() ); - if ( get_env ( "bytes_sent", envp ) !=NULL ){ - context->acctsocketbackgr.send(get_env("bytes_sent", envp)); - }else{ - context->acctsocketbackgr.send("0"); - } - if ( get_env ( "bytes_received", envp ) !=NULL ){ - context->acctsocketbackgr.send(get_env("bytes_received", envp)); - }else{ - context->acctsocketbackgr.send("0"); - } + context->acctsocketbackgr.send(DEL_USER); + context->acctsocketbackgr.send(newuser->getKey()); + if (get_env("bytes_sent", envp) != NULL) + { + context->acctsocketbackgr.send(get_env("bytes_sent", envp)); + } + else + { + context->acctsocketbackgr.send("0"); + } + if (get_env("bytes_received", envp) != NULL) + { + context->acctsocketbackgr.send(get_env("bytes_received", envp)); + } + else + { + context->acctsocketbackgr.send("0"); + } //get the response const int status = context->acctsocketbackgr.recvInt(); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Accounting for user with key" << newuser->getKey() << " stopped!\n"; + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Accounting for user with key" << newuser->getKey() << " stopped!\n"; //free the nasport - context->delNasPort ( newuser->getPortnumber() ); + context->delNasPort(newuser->getPortnumber()); //delete user from context - context->delUser ( newuser->getKey() ); + context->delUser(newuser->getKey()); return OPENVPN_PLUGIN_FUNC_SUCCESS; } else { - throw Exception ( "No user with this common_name!\n" ); - + throw Exception("No user with this common_name!\n"); } } - catch ( Exception &e ) + catch (Exception &e) { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" << e; } catch (std::bad_alloc) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_CLIENT_DISCONNECT" << endl; - } - catch ( ... ) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: New failed on UserPlugin in OPENVPN_PLUGIN_CLIENT_DISCONNECT" << endl; + } + catch (...) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" << "Unknown Exception!\n"; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" + << "Unknown Exception!\n"; } - } return OPENVPN_PLUGIN_FUNC_ERROR; } - /** The function is called when the OpenVpn process exits. * A exit command is send to the background processes and * the context is freed which was allocted in the open function. @@ -579,87 +559,86 @@ extern "C" OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_close_v1)(openvpn_plugin_handle_t handle) { //restore the context - PluginContext *context = ( PluginContext * ) handle; + PluginContext *context = (PluginContext *)handle; - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: close\n"; - if ( context->authsocketbackgr.getSocket() >= 0 ) + if (context->authsocketbackgr.getSocket() >= 0) { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: close auth background process\n"; //tell background process to exit try { - context->authsocketbackgr.send ( COMMAND_EXIT ); + context->authsocketbackgr.send(COMMAND_EXIT); } - catch ( Exception &e ) + catch (Exception &e) { cerr << getTime() << e; } - catch ( ... ) + catch (...) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" << "Unknown Exception!\n"; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" + << "Unknown Exception!\n"; } // wait for background process to exit - if ( context->getAuthPid() > 0 ) - waitpid ( context->getAuthPid(), NULL, 0 ); - + if (context->getAuthPid() > 0) + waitpid(context->getAuthPid(), NULL, 0); } - if ( context->acctsocketbackgr.getSocket() >= 0 ) + if (context->acctsocketbackgr.getSocket() >= 0) { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: close acct background process.\n"; //tell background process to exit try { - context->acctsocketbackgr.send ( COMMAND_EXIT ); + context->acctsocketbackgr.send(COMMAND_EXIT); } - catch ( Exception &e ) + catch (Exception &e) { cerr << getTime() << e; } - catch ( ... ) + catch (...) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" << "Unknown Exception!\n"; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND:" + << "Unknown Exception!\n"; } // wait for background process to exit - if ( context->getAcctPid() > 0 ) - waitpid ( context->getAcctPid(), NULL, 0 ); - + if (context->getAcctPid() > 0) + waitpid(context->getAcctPid(), NULL, 0); } - if (context->getStartThread()==false) //means the thread is running + if (context->getStartThread() == false) //means the thread is running { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Stop auth thread .\n"; - - //stop the threads + + //stop the threads pthread_mutex_lock(context->getMutexSend()); context->setStopThread(true); - pthread_cond_signal( context->getCondSend( )); + pthread_cond_signal(context->getCondSend()); pthread_mutex_unlock(context->getMutexSend()); pthread_mutex_lock(context->getAcctMutexSend()); - pthread_cond_signal( context->getAcctCondSend( )); + pthread_cond_signal(context->getAcctCondSend()); pthread_mutex_unlock(context->getAcctMutexSend()); - - - //wait for the thread to exit - if (context->conf.getAccountingOnly()==false) - pthread_join(*context->getThread(),NULL); - pthread_join(*context->getAcctThread(),NULL); - pthread_cond_destroy(context->getCondSend( )); - pthread_cond_destroy(context->getCondRecv( )); - pthread_mutex_destroy(context->getMutexSend()); - pthread_mutex_destroy(context->getMutexRecv()); - pthread_cond_destroy(context->getAcctCondSend( )); - pthread_cond_destroy(context->getAcctCondRecv( )); - pthread_mutex_destroy(context->getAcctMutexSend()); - pthread_mutex_destroy(context->getAcctMutexRecv()); + + //wait for the thread to exit + if (context->conf.getAccountingOnly() == false) + pthread_join(*context->getThread(), NULL); + pthread_join(*context->getAcctThread(), NULL); + pthread_cond_destroy(context->getCondSend()); + pthread_cond_destroy(context->getCondRecv()); + pthread_mutex_destroy(context->getMutexSend()); + pthread_mutex_destroy(context->getMutexRecv()); + pthread_cond_destroy(context->getAcctCondSend()); + pthread_cond_destroy(context->getAcctCondRecv()); + pthread_mutex_destroy(context->getAcctMutexSend()); + pthread_mutex_destroy(context->getAcctMutexRecv()); } else { @@ -668,13 +647,9 @@ extern "C" delete context; cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: DONE.\n"; - } - - } - /** Original function from the openvpn auth-pam plugin. * Given an environmental variable name, search * the envp array for its value, returning it @@ -684,22 +659,22 @@ extern "C" * @param The array with the environmental variables. * @return A poniter to the variable value or NULL, if the varaible was not found. */ -const char * get_env ( const char *name, const char *envp[] ) +const char *get_env(const char *name, const char *envp[]) { - if ( envp ) + if (envp) { int i; - const int namelen = strlen ( name ); + const int namelen = strlen(name); - for ( i = 0; envp[i]; ++i ) + for (i = 0; envp[i]; ++i) { //compare the environmental names - if ( !strncmp ( envp[i], name, namelen ) ) + if (!strncmp(envp[i], name, namelen)) { //if the varibale is found const char *cp = envp[i] + namelen; //return the value behind the = - if ( *cp == '=' ) + if (*cp == '=') return cp + 1; } } @@ -707,19 +682,17 @@ const char * get_env ( const char *name, const char *envp[] ) return NULL; } - - /** Original function from the openvpn auth-pam plugin. * Return the length of a string array. * @param Array * @return The length of the arry. */ -int string_array_len ( const char *array[] ) +int string_array_len(const char *array[]) { int i = 0; - if ( array ) + if (array) { - while ( array[i] ) + while (array[i]) ++i; } return i; @@ -736,14 +709,14 @@ int string_array_len ( const char *array[] ) * fds from crossing a fork(). * @param The socket number which should not be closed. */ -void close_fds_except ( int keep ) +void close_fds_except(int keep) { int i; - closelog (); - for ( i = 3; i <= 100; ++i ) + closelog(); + for (i = 3; i <= 100; ++i) { - if ( i != keep ) - close ( i ); + if (i != keep) + close(i); } } @@ -751,18 +724,17 @@ void close_fds_except ( int keep ) * Usually we ignore signals, because our parent will * deal with them. */ -void set_signals ( void ) +void set_signals(void) { - signal ( SIGTERM, SIG_DFL ); + signal(SIGTERM, SIG_DFL); - signal ( SIGINT, SIG_IGN ); - signal ( SIGHUP, SIG_IGN ); - signal ( SIGUSR1, SIG_IGN ); - signal ( SIGUSR2, SIG_IGN ); - signal ( SIGPIPE, SIG_IGN ); + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + signal(SIGPIPE, SIG_IGN); } - /** The function creates a md5 hash string as session ID over * - user->commonname * - user->callingstationid @@ -771,49 +743,48 @@ void set_signals ( void ) * @param A pointer to the user for which the session ID is created. * @return A string with the hash. */ -string createSessionId ( UserPlugin * user ) +string createSessionId(UserPlugin *user) { unsigned char digest[16]; - char text[33]; //The digest. - gcry_md_hd_t context; //the hash context + char text[33]; //The digest. + gcry_md_hd_t context; //the hash context int i; time_t rawtime; string strtime; ostringstream portnumber; - memset ( digest,0,16 ); - if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P)) + memset(digest, 0, 16); + if (!gcry_control(GCRYCTL_ANY_INITIALIZATION_P)) { /* No other library has already initialized libgcrypt. */ - gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread); + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) - { - cerr << "libgcrypt is too old (need " << NEED_LIBGCRYPT_VERSION << ", have " << gcry_check_version (NULL) << ")\n"; - } - /* Disable secure memory. */ - gcry_control (GCRYCTL_DISABLE_SECMEM, 0); - gcry_control (GCRYCTL_INITIALIZATION_FINISHED); + if (!gcry_check_version(NEED_LIBGCRYPT_VERSION)) + { + cerr << "libgcrypt is too old (need " << NEED_LIBGCRYPT_VERSION << ", have " << gcry_check_version(NULL) << ")\n"; + } + /* Disable secure memory. */ + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED); } //build the hash - gcry_md_open ( &context, GCRY_MD_MD5, 0 ); - gcry_md_write ( context, user->getCommonname().c_str(), user->getCommonname().length() ); - gcry_md_write ( context, user->getCallingStationId().c_str(), user->getCallingStationId().length() ); - gcry_md_write ( context, user->getUntrustedPort().c_str(), user->getUntrustedPort().length() ); - gcry_md_write ( context, user->getUntrustedPort().c_str(), user->getUntrustedPort().length() ); + gcry_md_open(&context, GCRY_MD_MD5, 0); + gcry_md_write(context, user->getCommonname().c_str(), user->getCommonname().length()); + gcry_md_write(context, user->getCallingStationId().c_str(), user->getCallingStationId().length()); + gcry_md_write(context, user->getUntrustedPort().c_str(), user->getUntrustedPort().length()); + gcry_md_write(context, user->getUntrustedPort().c_str(), user->getUntrustedPort().length()); portnumber << user->getPortnumber(); - gcry_md_write ( context,portnumber.str().c_str(), portnumber.str().length()); - time ( &rawtime ); - strtime=ctime ( &rawtime ); - gcry_md_write ( context, strtime.c_str(),strtime.length() ); - memcpy ( digest, gcry_md_read ( context, GCRY_MD_MD5 ), 16 ); - gcry_md_close ( context ); - - - unsigned int h,l; - char *p=text; - unsigned char *c=digest; - for ( i=0; i<16; i++ ) + gcry_md_write(context, portnumber.str().c_str(), portnumber.str().length()); + time(&rawtime); + strtime = ctime(&rawtime); + gcry_md_write(context, strtime.c_str(), strtime.length()); + memcpy(digest, gcry_md_read(context, GCRY_MD_MD5), 16); + gcry_md_close(context); + + unsigned int h, l; + char *p = text; + unsigned char *c = digest; + for (i = 0; i < 16; i++) { h = *c / 16; l = *c % 16; @@ -821,162 +792,161 @@ string createSessionId ( UserPlugin * user ) *p++ = "01234567890ABCDEF"[h]; *p++ = "01234567890ABCDEF"[l]; } - text[32]='\0'; - return string ( text ); + text[32] = '\0'; + return string(text); } - /** The function implements the thread for authentication. If the auth_control_file is specified the thread writes the results in the * auth_control_file, if the file is not specified the thread forward the OPENVPN_PLUGIN_FUNC_SUCCESS or OPENVPN_PLUGIN_FUNC_ERROR * to the main process. * @param _context The context pointer from OpenVPN. */ -void * auth_user_pass_verify(void * c) +void *auth_user_pass_verify(void *c) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Auth_user_pass_verify thread started."<< endl; - PluginContext * context = (PluginContext *) c; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Auth_user_pass_verify thread started." << endl; + PluginContext *context = (PluginContext *)c; //pthread_mutex_lock(context->getMutexSend()); //main thread loop for authentication //ignore signals - static sigset_t signal_mask; - sigemptyset (&signal_mask); - sigaddset (&signal_mask, SIGINT); - sigaddset (&signal_mask, SIGTERM); - sigaddset (&signal_mask, SIGHUP); - sigaddset (&signal_mask, SIGUSR1); - sigaddset (&signal_mask, SIGUSR2); - sigaddset (&signal_mask, SIGPIPE); - pthread_sigmask (SIG_BLOCK, &signal_mask, NULL); - + static sigset_t signal_mask; + sigemptyset(&signal_mask); + sigaddset(&signal_mask, SIGINT); + sigaddset(&signal_mask, SIGTERM); + sigaddset(&signal_mask, SIGHUP); + sigaddset(&signal_mask, SIGUSR1); + sigaddset(&signal_mask, SIGUSR2); + sigaddset(&signal_mask, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &signal_mask, NULL); while (!context->getStopThread()) { pthread_mutex_lock(context->getMutexSend()); - if (context->UserWaitingtoAuth()==false) + if (context->UserWaitingtoAuth() == false) { - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Waiting for new user." << endl; + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Waiting for new user." << endl; cout.flush(); - pthread_cond_wait(context->getCondSend(),context->getMutexSend()); + pthread_cond_wait(context->getCondSend(), context->getMutexSend()); } pthread_mutex_unlock(context->getMutexSend()); - if (context->getStopThread()==true) + if (context->getStopThread() == true) { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Stop signal received." << endl; break; } - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: New user from OpenVPN!" << endl; + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: New user from OpenVPN!" << endl; //is the user already known? - UserPlugin *olduser=NULL; /**getNewUser(); - olduser=context->findUser ( newuser->getKey() ); + olduser = context->findUser(newuser->getKey()); - if ( olduser!=NULL ) //probably key renegotiation + if (olduser != NULL) //probably key renegotiation { - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Renegotiation: username: "<< olduser->getUsername() + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Renegotiation: username: " << olduser->getUsername() << "\nRADIUS-PLUGIN: FOREGROUND THREAD:\t olduser ip: " << olduser->getCallingStationId() << "\nRADIUS-PLUGIN: FOREGROUND THREAD:\t olduser port: " << olduser->getUntrustedPort() << "\nRADIUS-PLUGIN: FOREGROUND THREAD:\t olduser FramedIP: " << olduser->getFramedIp() << "\nRADIUS-PLUGIN: FOREGROUND THREAD:\t newuser ip: " << olduser->getCallingStationId() << "\nRADIUS-PLUGIN: FOREGROUND THREAD:\t newuser port: " << olduser->getUntrustedPort() << "\n"; - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: isAuthenticated()" << olduser->isAuthenticated(); - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: isAcct()" << olduser->isAccounted(); + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: isAuthenticated()" << olduser->isAuthenticated(); + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: isAcct()" << olduser->isAccounted(); // update password and username, can happen when a new connection is established from the same client with the same port before the timeout in the openvpn server occurs! olduser->setPassword(newuser->getPassword()); olduser->setUsername(newuser->getUsername()); olduser->setAuthControlFile(newuser->getAuthControlFile()); //delete the newuser and use the olduser delete newuser; - newuser=olduser; + newuser = olduser; //TODO: for threading check if the user is already accounted (He must be for renegotiation) } else //new user for authentication, no renegotiation { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: New user." << endl; - newuser->setPortnumber ( context->addNasPort() ); - newuser->setSessionId ( createSessionId ( newuser ) ); + newuser->setPortnumber(context->addNasPort()); + newuser->setSessionId(createSessionId(newuser)); //add the user to the context context->addUser(newuser); } - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: New user: username: "<< newuser->getUsername() <<", password: *****" + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: New user: username: " << newuser->getUsername() << ", password: *****" << ", newuser ip: " << newuser->getCallingStationId() << ", newuser port: " << newuser->getUntrustedPort() << " ." << endl; //there must be a username - if ( newuser->getUsername().size() > 0 ) //&& olduser==NULL) + if (newuser->getUsername().size() > 0) //&& olduser==NULL) { //send the informations to the background process - context->authsocketbackgr.send ( COMMAND_VERIFY ); - context->authsocketbackgr.send ( newuser->getUsername() ); - context->authsocketbackgr.send ( newuser->getPassword() ); - context->authsocketbackgr.send ( newuser->getDev() ); - context->authsocketbackgr.send ( newuser->getPortnumber() ); - context->authsocketbackgr.send ( newuser->getSessionId() ); - context->authsocketbackgr.send ( newuser->getCallingStationId() ); - context->authsocketbackgr.send ( newuser->getCommonname() ); - context->authsocketbackgr.send ( newuser->getFramedIp() ); + context->authsocketbackgr.send(COMMAND_VERIFY); + context->authsocketbackgr.send(newuser->getUsername()); + context->authsocketbackgr.send(newuser->getPassword()); + context->authsocketbackgr.send(newuser->getDev()); + context->authsocketbackgr.send(newuser->getPortnumber()); + context->authsocketbackgr.send(newuser->getSessionId()); + context->authsocketbackgr.send(newuser->getCallingStationId()); + context->authsocketbackgr.send(newuser->getCommonname()); + context->authsocketbackgr.send(newuser->getFramedIp()); //get the response const int status = context->authsocketbackgr.recvInt(); - if ( status == RESPONSE_SUCCEEDED ) + if (status == RESPONSE_SUCCEEDED) { - if ( DEBUG ( context->getVerbosity() ) ) + if (DEBUG(context->getVerbosity())) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Authentication succeeded!" << endl; //get the routes from background process - newuser->setFramedRoutes ( context->authsocketbackgr.recvStr() ); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received routes for user: "<< newuser->getFramedRoutes() << "." << endl; + newuser->setFramedRoutes(context->authsocketbackgr.recvStr()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received routes for user: " << newuser->getFramedRoutes() << "." << endl; //get the framed ip - newuser->setFramedIp ( context->authsocketbackgr.recvStr() ); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received framed ip for user: "<< newuser->getFramedIp() << "." << endl; + newuser->setFramedIp(context->authsocketbackgr.recvStr()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received framed ip for user: " << newuser->getFramedIp() << "." << endl; //get the routes from background process - newuser->setFramedRoutes6 ( context->authsocketbackgr.recvStr() ); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received IPv6 routes for user: "<< newuser->getFramedRoutes6() << ".\n"; + newuser->setFramedRoutes6(context->authsocketbackgr.recvStr()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received IPv6 routes for user: " << newuser->getFramedRoutes6() << ".\n"; //get the framed IPv6 - newuser->setFramedIp6 ( context->authsocketbackgr.recvStr() ); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received framed IPv6 for user: "<< newuser->getFramedIp6() << "." << endl; - + newuser->setFramedIp6(context->authsocketbackgr.recvStr()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Received framed IPv6 for user: " << newuser->getFramedIp6() << "." << endl; // get the interval from the background process - newuser->setAcctInterimInterval ( context->authsocketbackgr.recvInt() ); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Receive acctinteriminterval " << newuser->getAcctInterimInterval() <<" sec from backgroundprocess." << endl; + newuser->setAcctInterimInterval(context->authsocketbackgr.recvInt()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Receive acctinteriminterval " << newuser->getAcctInterimInterval() << " sec from backgroundprocess." << endl; //clear the buffer if it isn't empty - if ( newuser->getVsaBuf() !=NULL ) + if (newuser->getVsaBuf() != NULL) { - delete [] newuser->getVsaBuf(); - newuser->setVsaBuf ( NULL ); + delete[] newuser->getVsaBuf(); + newuser->setVsaBuf(NULL); } // get the vendor specific attribute buffer from the background process - context->authsocketbackgr.recvBuf ( newuser ); + context->authsocketbackgr.recvBuf(newuser); //add the user to the context // if the is already in the map, addUser will throw an exception // only add the user if he it not known already - if ( newuser->isAuthenticated() ==false ) + if (newuser->isAuthenticated() == false) { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Add user to map." << endl; //save the success - newuser->setAuthenticated ( true ); + newuser->setAuthenticated(true); } - else if ( newuser->isAuthenticated() && olduser!=NULL ) + else if (newuser->isAuthenticated() && olduser != NULL) { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Don't add the user to the map, it is a rekeying." << endl; } - if (newuser->getAuthControlFile().length()>0 && context->conf.getUseAuthControlFile()) + if (newuser->getAuthControlFile().length() > 0 && context->conf.getUseAuthControlFile()) { write_control_file(context, newuser->getAuthControlFile(), '1'); } @@ -984,16 +954,14 @@ void * auth_user_pass_verify(void * c) { pthread_mutex_lock(context->getMutexRecv()); context->setResult(OPENVPN_PLUGIN_FUNC_SUCCESS); - - pthread_cond_signal( context->getCondRecv( )); - pthread_mutex_unlock (context->getMutexRecv()); + pthread_cond_signal(context->getCondRecv()); + pthread_mutex_unlock(context->getMutexRecv()); } - } else //AUTH failed { - /* should wait for disconnect call + /* should wait for disconnect call if ( newuser->isAccounted() ) //user is already known, delete him from the accounting { cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Error ar rekeying!" << endl; @@ -1018,7 +986,7 @@ void * auth_user_pass_verify(void * c) */ cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Error receiving auth confirmation from background process." << endl; - if (newuser->getAuthControlFile().length()>0 && context->conf.getUseAuthControlFile()) + if (newuser->getAuthControlFile().length() > 0 && context->conf.getUseAuthControlFile()) { write_control_file(context, newuser->getAuthControlFile(), '0'); } @@ -1026,25 +994,26 @@ void * auth_user_pass_verify(void * c) { pthread_mutex_lock(context->getMutexRecv()); context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - pthread_cond_signal( context->getCondRecv( )); - pthread_mutex_unlock (context->getMutexRecv()); + pthread_cond_signal(context->getCondRecv()); + pthread_mutex_unlock(context->getMutexRecv()); } //clean up: nas port, context, memory - if ( ! newuser->isAccounted() ){ - context->delNasPort(newuser->getPortnumber()); - context->delUser(newuser->getKey()); - delete newuser; - } + if (!newuser->isAccounted()) + { + context->delNasPort(newuser->getPortnumber()); + context->delUser(newuser->getKey()); + delete newuser; + } } } else { //clean up: nas port, context, memory context->delNasPort(newuser->getPortnumber()); - context->delUser (newuser->getKey()); + context->delUser(newuser->getKey()); //return OPENVPN_PLUGIN_FUNC_ERROR; - if (newuser->getAuthControlFile().length()>0 && context->conf.getUseAuthControlFile()) + if (newuser->getAuthControlFile().length() > 0 && context->conf.getUseAuthControlFile()) { write_control_file(context, newuser->getAuthControlFile(), '0'); } @@ -1052,9 +1021,9 @@ void * auth_user_pass_verify(void * c) { pthread_mutex_lock(context->getMutexRecv()); context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - - pthread_cond_signal( context->getCondRecv( )); - pthread_mutex_unlock (context->getMutexRecv()); + + pthread_cond_signal(context->getCondRecv()); + pthread_mutex_unlock(context->getMutexRecv()); } delete newuser; } @@ -1069,181 +1038,176 @@ void * auth_user_pass_verify(void * c) * to the main process. * @param _context The context pointer from OpenVPN. */ -void * client_connect(void * c) +void *client_connect(void *c) { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: client_connect thread started."<< endl; + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: client_connect thread started." << endl; //PluginContext * context = (PluginContext *) c; - PluginContext * context = (PluginContext *) c; + PluginContext *context = (PluginContext *)c; //pthread_mutex_lock(context->getMutexSend()); //main thread loop for authentication //ignore signals - static sigset_t signal_mask; - sigemptyset (&signal_mask); - sigaddset (&signal_mask, SIGINT); - sigaddset (&signal_mask, SIGTERM); - sigaddset (&signal_mask, SIGHUP); - sigaddset (&signal_mask, SIGUSR1); - sigaddset (&signal_mask, SIGUSR2); - sigaddset (&signal_mask, SIGPIPE); - pthread_sigmask (SIG_BLOCK, &signal_mask, NULL); - + static sigset_t signal_mask; + sigemptyset(&signal_mask); + sigaddset(&signal_mask, SIGINT); + sigaddset(&signal_mask, SIGTERM); + sigaddset(&signal_mask, SIGHUP); + sigaddset(&signal_mask, SIGUSR1); + sigaddset(&signal_mask, SIGUSR2); + sigaddset(&signal_mask, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &signal_mask, NULL); + while (!context->getStopThread()) { pthread_mutex_lock(context->getAcctMutexSend()); - if (context->UserWaitingtoAcct()==false) - { - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Waiting for new accounting user." << endl; - cout.flush(); - pthread_cond_wait(context->getAcctCondSend(),context->getAcctMutexSend()); - } - pthread_mutex_unlock(context->getAcctMutexSend()); - if (context->getStopThread()==true) - { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Stop signal received." << endl; - break; - } - - //find the user in the context, he was added at the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY - //string key=common_name + string ( "," ) +untrusted_ip+string ( ":" ) + string ( get_env ( "untrusted_port", envp ) ); - - UserPlugin *newuser=NULL; /**getNewAcctUser(); - newuser=context->findUser(tmpuser->getKey()); - if (newuser == NULL) + if (context->UserWaitingtoAcct() == false) + { + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Waiting for new accounting user." << endl; + cout.flush(); + pthread_cond_wait(context->getAcctCondSend(), context->getAcctMutexSend()); + } + pthread_mutex_unlock(context->getAcctMutexSend()); + if (context->getStopThread() == true) + { + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Stop signal received." << endl; + break; + } + + //find the user in the context, he was added at the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY + //string key=common_name + string ( "," ) +untrusted_ip+string ( ":" ) + string ( get_env ( "untrusted_port", envp ) ); + + UserPlugin *newuser = NULL; /**getNewAcctUser(); + newuser = context->findUser(tmpuser->getKey()); + if (newuser == NULL) + { + if (context->conf.getAccountingOnly() == true) //Authentication part is missing, where this is done else { - if (context->conf.getAccountingOnly()==true) //Authentication part is missing, where this is done else - { - newuser=tmpuser; - newuser->setAuthenticated(true); //the plugin does not care about it - newuser->setPortnumber ( context->addNasPort() ); - newuser->setSessionId ( createSessionId ( newuser ) ); - if (!newuser->getAcctInterimInterval()) - newuser->setAcctInterimInterval(context->conf.getDefAcctInterimInterval()); - //add the user to the context - context->addUser(newuser); - } - else - { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: User should be accounted but is unknown, should only occur if accountingonly=true.\n"; - pthread_mutex_lock(context->getAcctMutexRecv()); - context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - - pthread_cond_signal( context->getAcctCondRecv( )); - pthread_mutex_unlock (context->getAcctMutexRecv()); - } + newuser = tmpuser; + newuser->setAuthenticated(true); //the plugin does not care about it + newuser->setPortnumber(context->addNasPort()); + newuser->setSessionId(createSessionId(newuser)); + if (!newuser->getAcctInterimInterval()) + newuser->setAcctInterimInterval(context->conf.getDefAcctInterimInterval()); + //add the user to the context + context->addUser(newuser); } else - delete(tmpuser); - - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Set FramedIP to the IP (" << newuser->getFramedIp() << ") OpenVPN assigned to the user " << newuser->getUsername() << "\n"; - //the user must be there and must be authenticated but not accounted - // isAccounted and isAuthenticated is true it is client connect for renegotiation, the user is already in the accounting process - if ( newuser!=NULL && newuser->isAccounted() ==false && newuser->isAuthenticated() ) { - //transform the integers to strings to send them over the socket - - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Add user for accounting: username: " << newuser->getUsername() << ", commonname: " << newuser->getCommonname() << "\n"; - - //send information to the background process - context->acctsocketbackgr.send ( ADD_USER ); - context->acctsocketbackgr.send ( newuser->getUsername() ); - context->acctsocketbackgr.send ( newuser->getSessionId() ); - context->acctsocketbackgr.send ( newuser->getDev() ); - context->acctsocketbackgr.send ( newuser->getPortnumber() ); - context->acctsocketbackgr.send ( newuser->getCallingStationId() ); - context->acctsocketbackgr.send ( newuser->getFramedIp() ); - context->acctsocketbackgr.send ( newuser->getFramedIp6() ); - context->acctsocketbackgr.send ( newuser->getCommonname() ); - context->acctsocketbackgr.send ( newuser->getAcctInterimInterval() ); - context->acctsocketbackgr.send ( newuser->getFramedRoutes() ); - context->acctsocketbackgr.send ( newuser->getFramedRoutes6() ); - context->acctsocketbackgr.send ( newuser->getKey() ); - context->acctsocketbackgr.send ( newuser->getStatusFileKey()); - context->acctsocketbackgr.send ( newuser->getUntrustedPort() ); - - context->acctsocketbackgr.send ( newuser->getVsaBuf(), newuser->getVsaBufLen() ); - //get the response - const int status = context->acctsocketbackgr.recvInt(); - if ( status == RESPONSE_SUCCEEDED ) - { - newuser->setAccounted ( true ); + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: User should be accounted but is unknown, should only occur if accountingonly=true.\n"; + pthread_mutex_lock(context->getAcctMutexRecv()); + context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - if ( DEBUG ( context->getVerbosity() ) ) - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Accounting succeeded!\n"; + pthread_cond_signal(context->getAcctCondRecv()); + pthread_mutex_unlock(context->getAcctMutexRecv()); + } + } + else + delete (tmpuser); - if (newuser->getClientConnectDeferFile().length()>0 && context->conf.getUseClientConnectDeferFile()) - { - write_control_file(context, newuser->getClientConnectDeferFile(), '1'); - } - else - { - pthread_mutex_lock(context->getAcctMutexRecv()); - context->setResult(OPENVPN_PLUGIN_FUNC_SUCCESS); - - pthread_cond_signal( context->getAcctCondRecv( )); - pthread_mutex_unlock (context->getAcctMutexRecv()); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Set FramedIP to the IP (" << newuser->getFramedIp() << ") OpenVPN assigned to the user " << newuser->getUsername() << "\n"; + //the user must be there and must be authenticated but not accounted + // isAccounted and isAuthenticated is true it is client connect for renegotiation, the user is already in the accounting process + if (newuser != NULL && newuser->isAccounted() == false && newuser->isAuthenticated()) + { + //transform the integers to strings to send them over the socket + + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Add user for accounting: username: " << newuser->getUsername() << ", commonname: " << newuser->getCommonname() << "\n"; + + //send information to the background process + context->acctsocketbackgr.send(ADD_USER); + context->acctsocketbackgr.send(newuser->getUsername()); + context->acctsocketbackgr.send(newuser->getSessionId()); + context->acctsocketbackgr.send(newuser->getDev()); + context->acctsocketbackgr.send(newuser->getPortnumber()); + context->acctsocketbackgr.send(newuser->getCallingStationId()); + context->acctsocketbackgr.send(newuser->getFramedIp()); + context->acctsocketbackgr.send(newuser->getFramedIp6()); + context->acctsocketbackgr.send(newuser->getCommonname()); + context->acctsocketbackgr.send(newuser->getAcctInterimInterval()); + context->acctsocketbackgr.send(newuser->getFramedRoutes()); + context->acctsocketbackgr.send(newuser->getFramedRoutes6()); + context->acctsocketbackgr.send(newuser->getKey()); + context->acctsocketbackgr.send(newuser->getStatusFileKey()); + context->acctsocketbackgr.send(newuser->getUntrustedPort()); + + context->acctsocketbackgr.send(newuser->getVsaBuf(), newuser->getVsaBufLen()); + //get the response + const int status = context->acctsocketbackgr.recvInt(); + if (status == RESPONSE_SUCCEEDED) + { + newuser->setAccounted(true); - } + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Accounting succeeded!\n"; - //return OPENVPN_PLUGIN_FUNC_SUCCESS; + if (newuser->getClientConnectDeferFile().length() > 0 && context->conf.getUseClientConnectDeferFile()) + { + write_control_file(context, newuser->getClientConnectDeferFile(), '1'); } else { - //free the nasport - context->delNasPort ( newuser->getPortnumber() ); - string error; - error="RADIUS-PLUGIN: FOREGROUND THREAD: Accounting failed for user:"; - error+=newuser->getUsername(); - error+="!\n"; - cerr << getTime() << error; - //delete user from context - context->delUser ( newuser->getKey() ); - - if (newuser->getClientConnectDeferFile().length()>0 && context->conf.getUseClientConnectDeferFile()) - { - write_control_file(context, newuser->getClientConnectDeferFile(), '0'); - - } pthread_mutex_lock(context->getAcctMutexRecv()); - context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - pthread_cond_signal( context->getAcctCondRecv( )); - pthread_mutex_unlock (context->getAcctMutexRecv()); + context->setResult(OPENVPN_PLUGIN_FUNC_SUCCESS); + pthread_cond_signal(context->getAcctCondRecv()); + pthread_mutex_unlock(context->getAcctMutexRecv()); } + + //return OPENVPN_PLUGIN_FUNC_SUCCESS; } else { - cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: No user with this commonname or he is already authenticated\n"; + //free the nasport + context->delNasPort(newuser->getPortnumber()); + string error; + error = "RADIUS-PLUGIN: FOREGROUND THREAD: Accounting failed for user:"; + error += newuser->getUsername(); + error += "!\n"; + cerr << getTime() << error; + //delete user from context + context->delUser(newuser->getKey()); + + if (newuser->getClientConnectDeferFile().length() > 0 && context->conf.getUseClientConnectDeferFile()) + { + write_control_file(context, newuser->getClientConnectDeferFile(), '0'); + } pthread_mutex_lock(context->getAcctMutexRecv()); context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); - - pthread_cond_signal( context->getAcctCondRecv( )); - pthread_mutex_unlock (context->getAcctMutexRecv()); - + pthread_cond_signal(context->getAcctCondRecv()); + pthread_mutex_unlock(context->getAcctMutexRecv()); } + } + else + { + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: No user with this commonname or he is already authenticated\n"; + pthread_mutex_lock(context->getAcctMutexRecv()); + context->setResult(OPENVPN_PLUGIN_FUNC_ERROR); + + pthread_cond_signal(context->getAcctCondRecv()); + pthread_mutex_unlock(context->getAcctMutexRecv()); + } } pthread_mutex_unlock(context->getAcctMutexRecv()); pthread_mutex_unlock(context->getAcctMutexSend()); cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND THREAD: Thread finished.\n"; pthread_exit(NULL); - } - /** Writes the result of the authentication or accounting to the auth or client-connect control file (0: failure, 1: success). * @param filename The control file. * @param c The authentication result. */ -void write_control_file(PluginContext * context, string filename, char c) +void write_control_file(PluginContext *context, string filename, char c) { ofstream file; - file.open(filename.c_str(),ios::out); - if ( DEBUG ( context->getVerbosity() )) - cerr << getTime() << "RADIUS-PLUGIN: Write " << c << " to control file "<< filename << ".\n"; + file.open(filename.c_str(), ios::out); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: Write " << c << " to control file " << filename << ".\n"; if (file.is_open()) { file << c; @@ -1251,9 +1215,8 @@ void write_control_file(PluginContext * context, string filename, char c) } else { - cerr << getTime() << "RADIUS-PLUGIN: Could not open control file "<< filename << ".\n"; + cerr << getTime() << "RADIUS-PLUGIN: Could not open control file " << filename << ".\n"; } - } /** Returns the current time: @@ -1262,122 +1225,122 @@ void write_control_file(PluginContext * context, string filename, char c) string getTime() { time_t rawtime; - time ( &rawtime ); + time(&rawtime); string t(ctime(&rawtime)); - t.replace(t.find("\n"),1," "); - size_t str_pos=t.find("\n"); - if (str_pos!=string::npos) + t.replace(t.find("\n"), 1, " "); + size_t str_pos = t.find("\n"); + if (str_pos != string::npos) { - t.replace(str_pos,1," "); + t.replace(str_pos, 1, " "); } return t; } -void get_user_env(PluginContext * context,const int type,const char * envp[], UserPlugin * user) +void get_user_env(PluginContext *context, const int type, const char *envp[], UserPlugin *user) { - if ( get_env ( "username", envp ) ==NULL ) + if (get_env("username", envp) == NULL) { - if ( context->conf.getAccountingOnly() == false ) + if (context->conf.getAccountingOnly() == false) { - throw Exception ( "RADIUS-PLUGIN: FOREGROUND: username is not defined\n" ); + throw Exception("RADIUS-PLUGIN: FOREGROUND: username is not defined\n"); } - } - else if ( get_env ( "password", envp ) ==NULL ) + else if (get_env("password", envp) == NULL) { - if ( type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->conf.getAccountingOnly() == false ) + if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->conf.getAccountingOnly() == false) { - throw Exception ( "RADIUS-PLUGIN: FOREGROUND: password is not defined\n" ); + throw Exception("RADIUS-PLUGIN: FOREGROUND: password is not defined\n"); } - } - else if ( get_env ( "untrusted_ip", envp ) ==NULL && get_env ( "untrusted_ip6", envp ) ==NULL ) + else if (get_env("untrusted_ip", envp) == NULL && get_env("untrusted_ip6", envp) == NULL) { - throw Exception ( "RADIUS-PLUGIN: FOREGROUND: untrusted_ip and untrusted_ip6 is not defined\n" ); + throw Exception("RADIUS-PLUGIN: FOREGROUND: untrusted_ip and untrusted_ip6 is not defined\n"); } - else if ( get_env ( "common_name", envp ) ==NULL ) + else if (get_env("common_name", envp) == NULL) { - if ( context->conf.getClientCertNotRequired() == false ) + if (context->conf.getClientCertNotRequired() == false) { - throw Exception ( "RADIUS-PLUGIN: FOREGROUND: common_name is not defined\n" ); + throw Exception("RADIUS-PLUGIN: FOREGROUND: common_name is not defined\n"); } } - else if ( get_env ( "untrusted_port", envp ) ==NULL ) + else if (get_env("untrusted_port", envp) == NULL) { - throw Exception ( "RADIUS-PLUGIN: FOREGROUND: untrusted_port is not defined\n" ); + throw Exception("RADIUS-PLUGIN: FOREGROUND: untrusted_port is not defined\n"); } - - if (get_env ( "auth_control_file", envp ) != NULL) + if (get_env("auth_control_file", envp) != NULL) { - user->setAuthControlFile( get_env ( "auth_control_file", envp ) ); + user->setAuthControlFile(get_env("auth_control_file", envp)); } - if (get_env ( "client_connect_deferred_file", envp ) != NULL) + if (get_env("client_connect_deferred_file", envp) != NULL) { - user->setClientConnectDeferFile( get_env ( "client_connect_deferred_file", envp ) ); + user->setClientConnectDeferFile(get_env("client_connect_deferred_file", envp)); } // get username, password, unrusted_ip and common_name from envp string array // if the username is not defined and only accounting is used, set the username to the commonname - if ( get_env ( "username", envp ) !=NULL ) - user->setUsername ( get_env ( "username", envp ) ); + if (get_env("username", envp) != NULL) + user->setUsername(get_env("username", envp)); else if (context->conf.getAccountingOnly() == true) - user->setUsername ( get_env ( "common_name", envp ) ); - if ( get_env ( "password", envp ) !=NULL ) - user->setPassword ( get_env ( "password", envp ) ); - - if ( get_env ( "common_name", envp ) !=NULL ) + user->setUsername(get_env("common_name", envp)); + if (get_env("password", envp) != NULL) + user->setPassword(get_env("password", envp)); + + if (get_env("common_name", envp) != NULL) { - user->setCommonname ( get_env ( "common_name", envp ) ); + user->setCommonname(get_env("common_name", envp)); } - else if(context->conf.getClientCertNotRequired()==true) // if there is no username, set it to UNDEF, this is what OPENVPN does + else if (context->conf.getClientCertNotRequired() == true) // if there is no username, set it to UNDEF, this is what OPENVPN does { - user->setCommonname ("UNDEF"); + user->setCommonname("UNDEF"); } - + //rewrite the commonname if OpenVPN use the option username-as-common-name - if ( context->conf.getUsernameAsCommonname() == true ) + if (context->conf.getUsernameAsCommonname() == true) { - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Commonname set to Username\n"; - user->setCommonname ( get_env ( "username", envp ) ); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Commonname set to Username\n"; + user->setCommonname(get_env("username", envp)); } - user->setDev ( get_env ( "dev", envp ) ); + user->setDev(get_env("dev", envp)); string untrusted_ip; // it's ipv4 - if ( get_env ( "untrusted_ip", envp ) !=NULL ) + if (get_env("untrusted_ip", envp) != NULL) { - untrusted_ip = get_env ( "untrusted_ip", envp ); + untrusted_ip = get_env("untrusted_ip", envp); } // it's ipv6 else { - untrusted_ip = get_env ( "untrusted_ip6", envp ); + untrusted_ip = get_env("untrusted_ip6", envp); } - user->setCallingStationId ( untrusted_ip ); + user->setCallingStationId(untrusted_ip); //for OpenVPN option client cert not required, common_name is "UNDEF", see status.log //set the assigned ip as Framed-IP-Attribute of the user (see RFC2866, chapter 4.1 for more information) - if (get_env ( "ifconfig_pool_remote_ip", envp ) !=NULL) + if (get_env("ifconfig_pool_remote_ip", envp) != NULL) { - user->setFramedIp ( string ( get_env ( "ifconfig_pool_remote_ip", envp ) ) ); + user->setFramedIp(string(get_env("ifconfig_pool_remote_ip", envp))); } - if (get_env ( "ifconfig_ipv6_remote", envp ) !=NULL) + if (get_env("ifconfig_ipv6_remote", envp) != NULL) { - user->setFramedIp6 ( string ( get_env ( "ifconfig_ipv6_remote", envp ) ) ); + user->setFramedIp6(string(get_env("ifconfig_ipv6_remote", envp))); } - user->setUntrustedPort ( get_env ( "untrusted_port", envp ) ); - + user->setUntrustedPort(get_env("untrusted_port", envp)); + if (untrusted_ip.find(":") == untrusted_ip.npos) - user->setStatusFileKey(user->getCommonname() + string ( "," ) + untrusted_ip + string ( ":" ) + get_env ( "untrusted_port", envp ) ); + user->setStatusFileKey(user->getCommonname() + string(",") + untrusted_ip + string(":") + get_env("untrusted_port", envp)); else - user->setStatusFileKey(user->getCommonname() + string ( "," ) + untrusted_ip); + user->setStatusFileKey(user->getCommonname() + string(",") + untrusted_ip); - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: StatusFileKey: " << user->getStatusFileKey() << endl; - user->setKey(untrusted_ip + string ( ":" ) + get_env ( "untrusted_port", envp ) ); - if ( DEBUG ( context->getVerbosity() ) ) cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Key: " << user->getKey() << ".\n"; + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: StatusFileKey: " << user->getStatusFileKey() << endl; + user->setKey(untrusted_ip + string(":") + get_env("untrusted_port", envp)); + if (DEBUG(context->getVerbosity())) + cerr << getTime() << "RADIUS-PLUGIN: FOREGROUND: Key: " << user->getKey() << ".\n"; }