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

📄 sftpc.cpp

📁 伯克利做的SFTP安全文件传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
// sftpc.cc// SafeTP command-line (stand-alone) client// copyright SafeTP Development Group, Inc., 2000  Terms of use are as specified in license.txt#include <iostream.h>      // cout#include <stdlib.h>        // atoi, system#include <stdio.h>         // FILE functions#include <string.h>        // strstr#include <ctype.h>         // tolower, isspace#include <signal.h>        // signal#include "provider.h"      // SecurityProvider#include "exc.h"           // exceptions#include "base64t.h"       // base64transform#include "globrand.h"      // {read,save}RandomSeed#include "sockutil.h"      // socket funcs, INVALID_SOCKET#include "nonport.h"       // getCurrentUsername, readNonechoString#include "filesrc.h"       // FileInputSource#include "test.h"          // PVAL#include "glob.h"          // glob#include "strtokp.h"       // StrtokParse#include "keyutils.h"      // sm_testKey#include "syserr.h"        // xSysError#include "addent.h"        // getEntropyFromConsole#include "makekeys.h"      // GenerateElgamalKey#include "selgamal.h"      // ElGamal_1024bit_parameters#include "sdsa.h"          // DSABrandedPublicKey, knownDSAPublicKeyVersions#include "sftpver.h"       // SFTP_version#include "sftpcdoc.h"      // command documentation and aliases#include "sftpc.h"         // this module#ifndef NDEBUG#  define diagnostic(expr) printDiagnostic(stringb(expr))#else#  define diagnostic(expr) ((void)0)#endif// true: CRLF separates text lines; false: LF separates text lines#ifndef DEFAULT_LOCAL_CONVENTION#  ifdef __UNIX__#    define DEFAULT_LOCAL_CONVENTION false#  else   // windows#    define DEFAULT_LOCAL_CONVENTION true#  endif#endif#ifdef _MSC_VER                                                           // this one has to do with comparing signed and unsigned  #pragma warning(disable: 4018) // dob: I'm sick of seeing this warning    // this one is about 'this' appearing in a member initializer list,  // which I think is fine but MS apparently disagrees;  // TODO: look at spec to see what they say about 'this' in member inits  #pragma warning(disable: 4355) // dob: I'm sick of seeing this warning#endif// utilstring getCurrentUsername(){  char buf[80];  getCurrentUsername(buf, 80);  return string(buf);}string readNonechoString(char const *prompt){  char buf[80];  readNonechoString(buf, 80, prompt);  return string(buf);}// the initializers here should be maintained in the same// order, and with the same spacing, as they appear in// the class declarationSFTPC::SFTPC()  : security(NULL),    securityName("X-SafeTP1"),    keyDatabase(),    keyEnvironment(*this /*policy*/, keyDatabase),    serverPort(FTPD_PORT),    serverName("localhost"),    serverAddress(INADDR_NONE),    control(NULL),    dataBuffer(),    PBSZ(0),    digt(),    sentFirstAuth(false),    authenticated(false),    dataSecLevel(DSL_PRIVATE),    // default level    requestedPBSZ(0x8000),        // 32k    transferPassively(true),      // 9/17/00: new default for ubiquitous firewalls    asciiTransfers(false),    localConventionIsCRLF(DEFAULT_LOCAL_CONVENTION),    printOutgoing(false),    interactivePrompting(false),    quitProgram(false),    printHashMarks(false),    useRfc959(false),    acceptNewKeysSilently(false),    auto959Dropdown(false),    alwaysAcceptServerIPMismatch(false),    showDiagnostics(false),    showAdats(false),    useDebuggingLogin(false),    quitAfterNegotiation(false),    binaryTransferAnyway(false),    localGlobbing(true){}SFTPC::~SFTPC(){  if (security) {    delete security;  }  if (control) {    delete control;  }}void SFTPC::printDiagnostic(char const *msg){  if (showDiagnostics) {    cerr << msg << endl;  }}// read the next reply, and if it is encrypted, decrypt it; then return itReply SFTPC::readReply(){  // wait for the reply  diagnostic("waiting for server reply");  Reply reply(*control);  // look at reply code  ReplyCode code = reply.getCode();  // decrypt the message if necessary  if (first(code) == RCF_ENCRYPTED) {    diagnostic((int)reply.getCode() << " " << reply.getAllTextAsOneLine());    // it can only be encrypted if we're authenticated    xassert(authenticated);    // allocate and prepare block    string encoded = reply.getAllTextAsOneLine();      // todo: this is wrong for multiline responses...    int encodedLen = encoded.length();    int decodedLen = 1 +   // null at end      security->control().maximumDecodedSize(        base64decoder.maxOutputSize(encodedLen));    decodedLen = mymax(decodedLen, encodedLen);    DataBlock block(decodedLen);    block.setFromString(encoded);    // un-64    base64decoder.trans(block);    // decrypt    security->control().decode(block);    // add a null in case the reply didn't include    // a final CRLF (allowed by RFC 2228)    block.addNull();    // parse it as a reply, now that it's decoded    char const *temp = (char const*)block.getDataC();    Reply decryptedReply(temp);    // print it    cout << decryptedReply.getAllText();    // already has CRLF    return decryptedReply;  }  else {    // print reply    if (showAdats || !reply.containsADAT()) {      cout << reply.getAllText();             // already has CRLF    }    // it can only be plaintext if control channel unencrypted    xassert(!isControlChannelEncrypted());    // doesn't need to be decrypted    return reply;  }}Reply SFTPC::readFinalReply(){  // loop while the reply is intermediate  int intermediates = 0;  for(;;) {    // get and decrypt reply, then print it    Reply reply = readReply();    // do DIGT and ADAT processing    handleAdatAndDigt(reply);    // look at reply code    if (first(reply.getCode()) != RCF_POSITIVE_PRELIMINARY) {      return reply;    }    else {      intermediates++;      if (intermediates > 1) {        cout << "warning: server has violated FTP protocol by\n"             << "         sending " << intermediates             << " intermediate replies (1 is max)\n";      }    }  }}// we expect a reply; listen for the reply, decode it if// necessary, display it, then keep listening IF the reply// was a preliminary reply (otherwise return final code)ReplyCode SFTPC::readFinalReplyCode(){  return readFinalReply().getCode();}// get the reply, throw exception on error reply codeReplyCode SFTPC::readAndCheckFinalReplyCode(){  // read, print reply  Reply reply = readFinalReply();  // confirm is ok  checkReplyCode(reply);  // return the code itself  return reply.getCode();}// throw xReply if there is a problemvoid SFTPC::checkReplyCode(Reply const &reply) const{  ReplyCodeFirst f = first(reply.getCode());  if (!( f == RCF_POSITIVE_COMPLETION ||         f == RCF_POSITIVE_INTERMEDIATE )) {    xReply x(reply);    THROW(x);  }  // 6/7/99: I had been allowing RCF_POSITIVE_INTERMEDIATE, which  //         doesn't appear to be a possible return from readFinalReply.  // 6/8/99: doh!  it's "preliminary" which can't be returned;  //         "intermediate" can be (and is; e.g., "user" response is 331)}void SFTPC::handleAdatAndDigt(Reply &reply){  // the reply may go into the DIGT calculation  if (isDigtActive()) {    digt.add(reply);  }  // see if the reply contains some data  bool replyHasAdat = reply.containsADAT();  // process the ADAT if necessary  if (replyHasAdat) {    // make sure 'security' is expecting this    xassert(security &&            security->control().expectingIncomingAdat());    // get text    string text = reply.getAllTextAsOneLine();    // un-64 the reply text    DataBlock adat;    base64decode(adat, text + Reply::ADATTagLen);    // what did we get?    if (showAdats) {      adat.print("server adat");    }    // pass this to the security mechanism    security->control().incomingAdat(adat);  }}void SFTPC::sendRequestToWire(Request const &req){  // send the request  diagnostic("sending request: " << req.getText());  req.send(control->socket);  // possibly add it to the digest calculation  if (isDigtActive()) {    digt.add(req);  }}// send a request; this request is *not* encrypted as it is passed// don't process the reply; don't even take it from the socketvoid SFTPC::sendRequest(Request const &req){  if (printOutgoing) {    // this is how "ftp" does it    cout << "--> " << req.getTextNoPassword() << endl;  }  if (isControlChannelEncrypted()) {    // allocate and prepare buffer    string const &decoded = req.getText();    int decodedLen = decoded.length();    int encodedLen = 1 +       // null terminator      base64encoder.maxOutputSize(        security->control().maximumEncodedSize(decodedLen));    DataBlock block((byte const*)decoded.pcharc(), decodedLen,                    mymax(decodedLen, encodedLen));    // encrypt    security->control().encode(block);    // base64    base64encoder.trans(block);    // construct Request to hold it    Request encryptedReq(CMD_ENC,                         (char const*)block.getDataC());    // send it    diagnostic("sending plaintext request: " << req.getTextNoPassword());    sendRequestToWire(encryptedReq);  }  else {       // control channel is in the clear    sendRequestToWire(req);  }}// send the request and process the reply (expecting a success reply)void SFTPC::checkedRequest(Request const &req){  // send request  sendRequest(req);  // process reply  readAndCheckFinalReplyCode();}// request variantsvoid SFTPC::sendRequest(RequestCommand cmd, char const *args){  sendRequest(Request(cmd, args));}void SFTPC::checkedRequest(RequestCommand cmd, char const *args){  checkedRequest(Request(cmd, args));}// manages all communication from AUTH to "23{4,5} Security data exchange complete."bool SFTPC::negotiationPart2(){  // create the security provider  security = SecurityProvider::    findSecurityMechanism(securityName,                          &keyEnvironment,                          getLocalAddress(control->socket),                          getRemoteAddress(control->socket));  if (!security) {    xfailure(stringb("unknown security mechanism: " << securityName));  }  cout << "Starting negotiation...\n";  // send an auth request (will be added to digt automatically)  sentFirstAuth = true;  sendRequest(CMD_AUTH, securityName);  // get reply, process ADAT if included  ReplyCode authReplyCode = readFinalReplyCode();  if (first(authReplyCode) != RCF_POSITIVE_COMPLETION &&      first(authReplyCode) != RCF_POSITIVE_INTERMEDIATE) {    // AUTH not understood    delete security;    security = NULL;    return false;  }  // send an ADAT if necessary  while (security->control().hasOutgoingAdat()) {    // get data    DataBlock adat;    security->control().getNextOutgoingAdat(adat);    // what are we sending?    if (showAdats) {      adat.print("client adat");    }    // encode as base64    string b64adat = base64encode(adat);    // send it (will be added to digt automatically)    sendRequest(CMD_ADAT, b64adat);    // get reply, process ADAT if included    readAndCheckFinalReplyCode();  }  // if we don't have data to send, but are expecting some, that  // is an error, because we have no way to 'prompt' the server  // for more  xassert(!security->control().expectingIncomingAdat());  // from now on all control channel communication is encrypted  authenticated = true;  // compare digests  compareDigests();  cout << "Negotiation completed.\n";  return true;}void SFTPC::compareDigests(){  // ask for server's digest  sendRequest(CMD_DIGT);  // get server's reply  Reply reply = readFinalReply();  #if 0   // old; changed it after talking with Dan on 9/1/99    // check it (we require that the server support this command,    // because if we allow it not to, then a hacker has an easy    // way to defeat it)    xassert(first(reply.getCode()) == RCF_POSITIVE_COMPLETION);  #else    // we will allow the server to not support DIGT; the rationale    // here is that if a hacker can insert a bogus failure response,    // he/she can just as easily insert a bogus DIGT value; that is,    // we are using a threat model where the control channel is believed    // *authentic* but not necessarily *confidential*, as the former    // would require an on-line attack but the latter can utilize an    // off-line attack (e.g., on a weak encryption algorithm)    if (first(reply.getCode()) != RCF_POSITIVE_COMPLETION) {      cout << "Server doesn't understand DIGT command.  Proceeding anyway.\n";      return;    }  #endif  // find where the base-64 digest string starts  enum { DIGTEQLEN = 5 };    // length of "DIGT="  string replyText = reply.getAllTextAsOneLine();  char const *digteq = strstr(replyText, "DIGT=");  xassert(digteq);           // otherwise the server didn't include a digest!

⌨️ 快捷键说明

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