fifoargs.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       fifoargs.cc
00003 ///             Class for passing command line arguments via fifo instead
00004 ///             of command line.
00005 ///
00006 
00007 /*
00008     Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "fifoargs.h"
00024 #include "error.h"
00025 #include "common.h"
00026 #include <iostream>
00027 #include <sstream>
00028 #include <iomanip>
00029 #include <string>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <fcntl.h>
00033 #include <unistd.h>
00034 #include <errno.h>
00035 
00036 using namespace std;
00037 
00038 namespace Barry {
00039 
00040 //////////////////////////////////////////////////////////////////////////////
00041 // FifoArgs class
00042 
00043 std::ostream& FifoArgs::Write(std::ostream &os) const
00044 {
00045         if( m_pin.Valid() )
00046                 os << "Pin " << m_pin.Str() << endl;
00047         if( m_password.size() )
00048                 os << "Password " << m_password << endl;
00049         if( m_log_filename.size() )
00050                 os << "LogFilename " << m_log_filename << endl;
00051         if( m_use_serial_mode )
00052                 os << "UseSerialMode" << endl;
00053         if( m_verbose )
00054                 os << "Verbose" << endl;
00055         
00056         return os;
00057 }
00058 
00059 std::istream& FifoArgs::Read(std::istream &is)
00060 {
00061         string line, token, arg;
00062 
00063         // start fresh
00064         Clear();
00065 
00066         while( getline(is, line) ) {
00067                 istringstream iss(line);
00068                 iss >> token >> ws;
00069 
00070                 if( token == "Pin" )
00071                         iss >> m_pin;
00072                 else if( token == "Password" )
00073                         getline(iss, m_password);
00074                 else if( token == "LogFilename" )
00075                         getline(iss, m_log_filename);
00076                 else if( token == "UseSerialMode" )
00077                         m_use_serial_mode = true;
00078                 else if( token == "Verbose" )
00079                         m_verbose = true;
00080         }
00081 
00082         return is;
00083 }
00084 
00085 void FifoArgs::Clear()
00086 {
00087         m_pin.Clear();
00088         m_password.clear();
00089         m_log_filename.clear();
00090         m_use_serial_mode = false;
00091         m_verbose = false;
00092 }
00093 
00094 
00095 //////////////////////////////////////////////////////////////////////////////
00096 // FifoServer class
00097 
00098 FifoServer::FifoServer(const FifoArgs &args)
00099         : m_args(args)
00100         , m_created(false)
00101 {
00102         int m_fifo = mkfifo(BARRY_FIFO_NAME, 0660);
00103         if( m_fifo != 0 )
00104                 throw ErrnoError("Cannot open Barry argument fifo", errno);
00105         m_created = true;
00106 }
00107 
00108 FifoServer::~FifoServer()
00109 {
00110         Cleanup();
00111 }
00112 
00113 bool FifoServer::Serve(int timeout_sec)
00114 {
00115         if( !m_created )
00116                 return false;
00117 
00118         // man fifo(7) says that opening write-only in non-blocking mode
00119         // will fail until the other side opens for read.  So continue
00120         // to attempt opens until out of time.
00121         timeout_sec *= 4;
00122         while( timeout_sec-- ) {
00123                 // attempt to open in non-blocking mode
00124                 //
00125                 // Security Note:
00126                 // --------------
00127                 // This should be safe from symlink attacks, since
00128                 // mkfifo(), in the constructor, will fail if any other
00129                 // file or symlink already exists, and therefore we will
00130                 // never get to this open() call if that fails.  And if
00131                 // mkfifo() succeeds, then we are guaranteed (assuming /tmp
00132                 // permissions are correct) that only root or our own
00133                 // user can replace the fifo with something else, such
00134                 // as a symlink.
00135                 //
00136                 // The server side is not intended to run as root, yet
00137                 // if it is, then we are still safe, due to the above logic.
00138                 // The client side can run as root (depending on what pppd
00139                 // does with pppob), and has no control over creation of
00140                 // the fifo, but only opens it for reading, never for
00141                 // creation or writing. (See FifoClient() below.)
00142                 //
00143                 // Therefore, we can only be attacked, via symlink,
00144                 // by root or ourselves.
00145                 //
00146                 int fd = open(BARRY_FIFO_NAME, O_WRONLY | O_NONBLOCK);
00147                 if( fd == -1 ) {
00148                         usleep(250000);
00149                         continue;
00150                 }
00151 
00152                 ostringstream oss;
00153                 m_args.Write(oss);
00154                 int written = write(fd, oss.str().data(), oss.str().size());
00155                 close(fd);
00156 
00157                 // only success if we wrote all the data
00158                 return written == (int)oss.str().size();
00159         }
00160 
00161         // timeout
00162         return false;
00163 }
00164 
00165 void FifoServer::Cleanup()
00166 {
00167         if( m_created ) {
00168                 unlink(BARRY_FIFO_NAME);
00169                 m_created = false;
00170         }
00171 }
00172 
00173 
00174 //////////////////////////////////////////////////////////////////////////////
00175 // FifoClient class
00176 
00177 FifoClient::FifoClient()
00178 {
00179 }
00180 
00181 /// Tries to open the fifo and read the arguments from it.
00182 /// If it fails in any way, or timeout, returns false.
00183 bool FifoClient::Fetch(int timeout_sec)
00184 {
00185         // See man fifo(7).  Should always succeed, as long as
00186         // the file exists and permissions allow.
00187         int fd = open(BARRY_FIFO_NAME, O_RDONLY | O_NONBLOCK);
00188         if( fd == -1 )
00189                 return false;
00190 
00191         string sbuf;
00192         timeout_sec *= 4;
00193         while( timeout_sec-- ) {
00194                 char buf[4096];
00195                 int r = read(fd, buf, sizeof(buf));
00196 
00197                 if( r == 0 ) {
00198                         // only consider this the end of file if
00199                         // we've already read something, otherwise we close
00200                         // before the server has a chance to speak up
00201                         if( sbuf.size() )
00202                                 break;
00203                         else
00204                                 usleep(250000);
00205                 }
00206                 else if( r < 0 ) {
00207                         usleep(250000);
00208                         continue;
00209                 }
00210                 else {
00211                         timeout_sec++;
00212                         sbuf.append(buf, r);
00213                 }
00214         }
00215         close(fd);
00216 
00217         // parse
00218         istringstream iss(sbuf);
00219         m_args.Read(iss);
00220         return true;
00221 }
00222 
00223 } // Barry namespace
00224