⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sftpc.cpp

📁 伯克利做的SFTP安全文件传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
  // decode the base-64 data  DataBlock serverDigest;  base64decode(serverDigest, digteq + DIGTEQLEN);  //int foobarbaz = replyText.length();  // ?  //serverDigest.print("server's DIGT");  // retrieve our own digest value  DataBlock clientDigest = digt.getDigt();  //clientDigest.print("client's DIGT");  // compare  if (serverDigest != clientDigest) {    // This is the first of several similarly-formatted messages that    // sftpc can generate.  The idea here is to blow the whistle rather    // loudly, so an attacker considering mounting an attack that would    // provoke such a message will decide it's too high-profile.  Of    // course, for this to work, the false-positive rate must be low.    cout << "\n"            "         ************************\n"            "         *   Digest Mismatch!   *\n"            "         ************************\n"            "\n"            "The server saw a different negotiation sequence than I\n"            "did.  This could be the result of someone tampering with\n"            "the data while in transit.  You could just try again...\n"            "If the problem persists, contact your network administrator;\n"            "the situation must be resolved before communications with\n"            "this server can be trusted.\n"            "\n"            ;    xfailure("Connection aborted due to digest mismatch.");  }}// implements dropdown behaviorbool SFTPC::negotiationPart1(int &retval){  #define FAILRETURN(val) retval=val; return false /* user-; */  // do security mechanism negotiation  if (!useRfc959) {    if (!negotiationPart2()) {      // server responded negatively to AUTH command      if (keyDatabase.containsKey(serverAddress)) {        cout << "\n"                "        ************************\n"                "        *   Dropdown Attack!   *\n"                "        ************************\n"                "\n"                "The server claims to not understand the AUTH mechanism,\n"                "but we have a key on record for this server!  Possibilities:\n"                "  - An attacker is attempting to fool sftpc into using an\n"                "    insecure protocol.\n"                "  - SafeTP has been turned off at the server.\n"                "  - The prior contact was on another port; did you try 353?\n"                "If you want to force sftpc to connect insecurely anyway, use\n"                "the -9 switch.\n"                ;        FAILRETURN(5);      }      else if (auto959Dropdown) {        cout << "Server doesn't understand AUTH mechanism.  Reverting\n"                "to insecure protocol (because -Q was specified).\n";        useRfc959 = true;      }      else {        cout << "Exiting because server doesn't understand AUTH mechanism.\n"                "There are several ways to proceed:\n"                "  - Connect to the server using the normal (insecure) FTP\n"                "    protocol.  To do this, give sftpc the -9 flag.\n"                "    Be aware that your password might be eavesdropped!\n"                "  - Use a completely different system, such as scp or Kerberos.\n"                "  - Try a different port.  In particular, some sites have\n"                "    SafeTP on port 353 instead of the default port (21):\n"                "      % sftpc the.server 353\n"                ;        FAILRETURN(4);     // failed negotiation      }    }    else {      // after successful negotiation, do data channel protection,      // if the user wants something other than cleartext      if (dataSecLevel != DSL_CLEAR &&          setDataEncryption(dataSecLevel)) {        // successfully set prot level; we will assume the server's        // printed response is adequate to inform user      }      else {        dataSecLevel = DSL_CLEAR;        printf("Data channel is currently unencrypted (change with 'prot').\n");      }    }  }  #undef FAILRETURN  return true;     // success}// ------------------ data channel stuff ---------------------// bind a listener socket, tell server about it, return itSOCKET SFTPC::setupActiveConnection(){  // bind and listen to a socket  SOCKET dataListener = listen_socket(PORT_ANY);  // determine which port was bound  sockaddr_in localPortAddr;  getSockName(dataListener, localPortAddr);  // determine the IP address by which the server knows us  sockaddr_in localIPAddr;  getSockName(control->socket, localIPAddr);  // format a string to contain this information for  // the PORT command  long ip = ntohl(localIPAddr.sin_addr.s_addr);  int port = ntohs(localPortAddr.sin_port);  diagnostic("bound socket to local port " << port);  // write the a,a,a,a,p,p string  string commandText = constructPortArg(ip, port);  // send the PORT command, process the reply  checkedRequest(CMD_PORT, commandText);  // return the dataListener socket  return dataListener;}// ask the server to bind a listener, and connect to it, and// return the connected socket (this relies on the server being// able to accept() a connection even though the corresponding// data transfer hasn't yet begun)SOCKET SFTPC::setupPassiveConnection(){  // send PASV command  sendRequest(CMD_PASV);  // get reply  Reply reply = readReply();  checkReplyCode(reply);       // minor fix: w/o this, errors produce a confusing message  // parse the address and port  IPAddress addr;  int port;  parsePasvArgument(reply.getAllTextAsOneLine(),                    addr, port);  // connect to that address and port  diagnostic("trying to connect to " << formatAddress(addr) <<             ", port " << port);  return connect_socket(addr, port);}// ask the server to switch to a specific file transfer mode// (ascii or image (binary))void SFTPC::requestAsciiMode(bool ascii){  // debugging: to test my CRLF routines, I want to have the server send  //            files in binary, but treat them as if they were sent as  //            ascii  if (binaryTransferAnyway) {    ascii = false;  }  // send TYPE A (ascii) or TYPE I (image/binary),  //   and verify the server is happy with that  checkedRequest(CMD_TYPE, ascii? "A" : "I");  // possible optimization: could remember what mode we think  // the server is in, and only change it if it's different than  // what we want; my usual ftp client does *not* do this  // optimization, which makes me suspect some servers could be  // flaky about remembering the transfer mode state...}// active: send PORT and return listening socket// passive: send PASV and return connected data channel socketSOCKET SFTPC::dataChannelSetup(RequestCommand cmd){  // set transfer mode  requestAsciiMode(transferModeForCmd(cmd));  if (transferPassively) {    // PASV and connect()    return setupPassiveConnection();  }  else {    // PORT and listen()    return setupActiveConnection();  }}bool SFTPC::transferModeForCmd(RequestCommand cmd) const{  if (cmd == CMD_LIST || cmd == CMD_NLST) {    // always do directory tranfers in ASCII mode    return true;  }  else {    // file transfers are in mode of user's choosing    return asciiTransfers;  }}// second phase of establishing a data connection, given// that dataChannelSetup did first part, and 'dataListener'// is its return valueSOCKET SFTPC::openDataChannel(SOCKET dataListener){  // get the data channel  if (!transferPassively) {    // accept the connection    diagnostic("waiting for server to connect to my data port");    SOCKET dataChannel = accept_socket(dataListener);    diagnostic("server connected: " << dataChannel);    // stop listening    close_socket(dataListener);    return dataChannel;  }  else {    // the dataListener is actually the already-connected socket    return dataListener;  }}void SFTPC::check150Reply(){  diagnostic("waiting for the 125/150");  Reply interReply = readReply();  ReplyCode code = interReply.getCode();  if (first(code) == RCF_POSITIVE_PRELIMINARY) {    diagnostic("got the 125/150 as expected");  }  else if (first(code) == RCF_POSITIVE_COMPLETION ||           first(code) == RCF_POSITIVE_INTERMEDIATE) {    diagnostic("oops.. that was supposed to be a 125/150");    printf("unexpected reply: %d %s\n",            code, interReply.getAllTextAsOneLine().pcharc());    // try to keep going anyway  }  else {    // since the reply is an error, its text will already have    // been printed    xReply x(interReply);    THROW(x);    // not reached  }}SOCKET SFTPC::startDataTransfer(RequestCommand cmd, char const *arg){  // transfer mode, then PORT or PASV  SOCKET dataListener = dataChannelSetup(cmd);  // protocol semantics are that the incrementing is associated  // with the *attempt*, so just before we send the command, we  // increment (if the send itself fails, then we've lost our  // connection to the server anyway)  if (security && isDataChannelEncrypted()) {    security->data().newFile(dataSecLevel);  }  // send the primary request  sendRequest(cmd, arg);  check150Reply();  // get channel from listener  SOCKET dataChannel = openDataChannel(dataListener);  // setup buffer  if (!isDataChannelEncrypted() &&      dataBuffer.getAllocated() < UNENCRYPTED_BUFSIZE) {        // this was the site of a bug in 1.20, because I was not testing        // the sizes; thus, turning encryption off and back on would        // truncated dataBuffer, causing an exception fail on data xfer     dataBuffer.setAllocated(UNENCRYPTED_BUFSIZE);  }  return dataChannel;}// convenient way to ensure a socket gets closed, so the protocol// stream remains viableclass SocketCloser {  SOCKET s;public:  SocketCloser(SOCKET sk) : s(sk) {}  ~SocketCloser();};SocketCloser::~SocketCloser(){  close_socket(s);}// do a data transfer//   cmd              arg                    localFname//   ------           -------------------    -----------------//   retr             remote fname           local fname to write to//   stor,stou,appe   remote fname           local fname to read from//   list,nlst        optional dir to list   unusedvoid SFTPC::dataTransfer(RequestCommand cmd, char const *arg,                         char const *localFname){  // check local file first  FILE *fp = NULL;  if (cmd == CMD_RETR || cmd == CMD_STOR ||      cmd == CMD_STOU || cmd == CMD_APPE) {    // open the file    xassert(localFname);    fp = fopen(localFname, cmd==CMD_RETR? "wb" : "rb");    if (!fp) {      THROW(xBase(stringb("failed to open local file " << localFname)));    }  }  // dataChannel scope  {    SOCKET dataChannel;    try {      // PORT, etc.      dataChannel = startDataTransfer(cmd, arg);    }    catch (...) {   // common cause: server says file doesn't exist for RETR      if (fp != NULL) {        // Dan caught a bug here.. wasn't checking for fp == NULL..        fclose(fp);     // may as well, since we need this before removal      }      if (cmd == CMD_RETR) {        // we already deleted any existing file, and now we failed to get        // it from the server, so it's 0-length; go ahead and remove it        removeFile(localFname);      }      diagnostic("re-throwing xReply");      throw;          // just relay the error condition itself    }    SocketCloser closer(dataChannel);    // do transfer    bool transferMode = transferModeForCmd(cmd);    switch (cmd) {      case CMD_RETR: {        // copy data from server to client        FileOutputDest destStream(fp);        // copy data from the socket to the file until the        // socket is closed        int totlen;        try {	  if (!printHashMarks) {	    totlen = copyFromSocketToStream(	      dataChannel, destStream, transferMode);	  }	  else {	    EchoOutputStream echo(destStream);	    totlen = copyFromSocketToStream(	      dataChannel, echo, transferMode);	    cout << endl;	  }        }        catch (xBase &x) {          cout << "WARNING: file " << localFname << " is probably "                  "incomplete.\n";                            // something subtle is happening here -- if the data channel is          // closed unexpectedly, an xSocket is thrown.. if the toplevel          // loop sees an xSocket, it assumes it was caused by a *control*          // connection closure.. so we catch it here, and re-throw it as          // an xBase (since C++ exceptions are typed statically), which          // the toplevel loop (counterintuitively) assumes is less          // serious          throw x;                      // all this nonsense exposes a serious flaw in the way I've          // designed my exception-handling here.. unfortunately, I am          // unaware of any techniques for improving the design, since          // it's very difficult to reason about...        }        diagnostic("received " << totlen <<                   " bytes to local file " << localFname);        fclose(fp);        break;      }      case CMD_STOR:      case CMD_STOU:      case CMD_APPE: {        // all the commands that copy data from the client to        // the server        FileInputSource sourceStream(fp);        // copy data from the file to the socket        int totlen;        try {	  if (!printHashMarks) {	    totlen = copyFromStreamToSocket(	      sourceStream, dataChannel, transferMode);	  }	  else {	    EchoInputStream echo(sourceStream);	    totlen = copyFromStreamToSocket(	      echo, dataChannel, transferMode);	    cout << endl;	  }        }        catch (xBase &x) {          cout << "WARNING: remote file " << arg << " is probably "                  "incomplete.\n";          throw x;     // see above        }        diagnostic("sent " << totlen <<                   " bytes from local file " << localFname);        fclose(fp);        break;      }      case CMD_LIST:      case CMD_NLST: {        // server sends data that is displayed on client's console        diagnostic("waiting for directory listing");        // copy data from the socket to the terminal until the        // socket is closed        FileOutputDest destStream(stdout);        int totlen = copyFromSocketToStream(dataChannel, destStream,                                            transferMode);        diagnostic("received " << totlen <<                   " bytes as directory listing");        break;      }      default:        xfailure("dataTransfer called with bad ftp command");    }    // dataChannel closed by closer  }  // get and check final 226  readAndCheckFinalReplyCode();}STATICDEF void SFTPC::CRLF_to_LF(  StreamInputSource &src, StreamOutputDest &dest){  CRLF_to_something(src, dest, ::CRLF_to_LF);}STATICDEF void SFTPC::CRLF_to_CRLF(  StreamInputSource &src, StreamOutputDest &dest){  CRLF_to_something(src, dest, ::CRLF_to_CRLF);}STATICDEF void SFTPC::CRLF_to_something(

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -