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

📄 sftpd.cpp

📁 伯克利做的SFTP安全文件传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
    case CMD_USER:    case CMD_ACCT: {      if (state != STATE_UNAUTHENTICATED) {        clientReply(RC_PROTECTION_LEVEL_DENIED,          "To enter RFC 959 (unencrypted) compatibility mode, "          "USER or ACCT must be the *first* command.");        break;      }      char const *loginName = request.getData();      if (checkLoginName(loginName)) {        break;    // it took action      }      // this is the *only* place that 'state' might be changed to      // STATE_959_INTEROP, and therefore the only place that needs to      // check allowRfc959      if (allowRfc959  ||          (allowRfc959_anon && isAnonLogin(loginName))) {        // 959 dropdown        state = STATE_959_INTEROP;        log(LE_HANDOFF, "dropping down to 959 compatibility mode");        // call this function recursively to handle 959 command        handleOriginalRequest(request);      }      else {        // disallow        Reply r(RC_PROTECTION_LEVEL_DENIED,          "Must send AUTH first.  RFC 959 compatibility is off.");        r.append("This is a secure FTP server, and you are running an insecure client.");        r.append("You can get software to secure your FTP client at:");        r.append("http://safetp.cs.berkeley.edu/");        clientReply(r);      }      break;    }    case CMD_QUIT:    case CMD_HELP:         // could offer info about AUTH/ADAT here...      // relay to server, regardless of our state      send(request, server_control);      break;    case CMD_MIC:      // supported as if it is ENC    case CMD_CONF:     // same deal    case CMD_ENC: {      if (!controlChannelEncrypted()) {        clientReply(RC_BAD_COMMAND_SEQUENCE,          "Cannot send encrypted requests until authenticated.");        break;      }      xassert(security);      try {        // allocate and prepare the translation buffer        int requestLen = base64length(request.getData());        int bufferLen =          security->control().maximumDecodedSize(            base64decoder.maxOutputSize(requestLen)) + 1;        bufferLen = mymax(bufferLen, requestLen);        DataBlock requestBlock((byte const*)request.getData(),          requestLen, bufferLen);        // un-64 the request        base64decoder.trans(requestBlock);        // decrypt the request        security->control().decode(requestBlock);        // add a null terminator        requestBlock.addNull();        // debugging output; note that the password protection offered        // here is NOT sufficient to conceal the password from the        // debug output (because debug output has keys, etc.)        Request newReq((char const*)requestBlock.getDataC());        diagnostic("decrypted request: " <<                   newReq.getTextNoPassword());        // handle the request        handleDecryptedRequest(newReq);      }      catch (xSecurity &x) {        log(LE_WARNING, "SECURITY VIOLATION: " << x.why());        // go ahead and tell the client.. if a hacker is really        // present, this may not get there; but the hacker won't        // be able to decrypt it, so there's no harm done (unless        // this reveals something critical about the internal        // state to a client who is a hacker...)        clientReply(          Reply(RC_FAILED_SECURITY_CHECK, x.why()));        // but, since we may be being hacked, and recovery from things        // like sequence # invalid is not generally possible, close        // the connection        closeConnections();      }      break;    }    case CMD_UNKNOWN:      clientReply(RC_UNRECOGNIZED_COMMAND,        stringb("I don't understand \"" << request.getCommand() << "\"."));      break;    default:      // firewall friendliness:  The issue here is that while many      // firewalls (including NAT firewalls) already have provisions      // for allowing FTP connections to penetrate, but when we      // encrypt the control channel, we hide the protocol commands      // necessary for the firewall to know which ports to open.      //      // So, a solution that works with at least some firewalls      // (specifically known to work with Linux IP masquerading and      // Windows Internet Connection Sharing) is to send redundant      // control traffic in the clear.  When the firewall sees this      // traffic, it will open the right ports.  The same commands      // are repeated within in the integrity- and privacy-protected      // control channel.      //      // This traffic is *ignored* by the state machine logic on both      // sides -- it is purely for the firewall to act upon.  An      // attacker who sees the traffic learns nothing useful *if* data      // encryption is on; an attacker who modifies this traffic      // simply DOSes the connection.      #if 1      // set to 0 to disable firewall-friendliness behavior        bool supportClientSideFirewallFriendliness = true;        if (supportClientSideFirewallFriendliness) {          if (request.getCmd() == CMD_PORT) {            string portstr = request.getData();            // be careful to strip off any trailing garbage            while (portstr.length() && !isdigit(portstr[portstr.length()-1])) {              portstr = string(portstr.pchar(), portstr.length()-1);            }            stringBuilder s("Accepting cleartext PORT command to provide client-side firewall friendliness.");            s << " (PORT=" << portstr << ")";            unencryptedClientReply(Reply(RC_COMMAND_OK, s));            break;          }          else if (request.getCmd() == CMD_LIST) {            // we accept LIST too because there might be a firewall that            // doesn't open the ports until it sees a real data-transfer            // command; since no directory list is actually returned, this            // is presumably safe            unencryptedClientReply(Reply(RC_FILE_STATUS_OK,              "Accepting cleartext LIST command to provide client-side firewall friendliness."));            break;          }        }      #endif      clientReply(RC_PROTECTION_LEVEL_DENIED,        "I will not accept cleartext commands.");      break;  }}// the returned value is used in two places://   - as the address supplied in the protocol stream during negotiation;//     the client checks this against the address it specified in the//     connect() call//   - as the address in a reply to PASV; the client will then try to//     connect to the address during a data transferIPAddress SFTPD::getMyIPAsClientSeesIt() const{  // decide which local address to claim; normal case (no firewalls,  // etc.) is it's just my control channel interface address  IPAddress localToUse = getLocalAddress(client_control);  // -i: use of an alternate ip address  if ( useFakeIPAddress &&       (getLocalAddress(client_control) != getRemoteAddress(client_control)) ) {    // second condition is so localhost isn't faked out    // (for testing purposes)    localToUse = fakeIPAddress;  }  return localToUse;}void SFTPD::closeConnections(){  closeIf(client_control);  closeIf(server_control);  closeDataSockets();}// in addition to being called from closeConnections(), this is called// when we received a PORT or PASV command; it is needed because if// e.g. we got two PASV commands in a row, we want to shut down the// sockets associated with the first before opening new ones (usually// on different ports) for the secondvoid SFTPD::closeDataSockets(){  closeIf(client_data);  closeIf(server_data);  closeIf(client_listen);  closeIf(server_listen);}void SFTPD::closeIf(SOCKET &s){  if (s != INVALID_SOCKET) {    closeSocket(s);  }}void SFTPD::closeSocket(SOCKET &s){  socket_diagnostic("closing socket " << s);  close_socket(s);  s = INVALID_SOCKET;}// handle a request that was encryped (it has now been decrypted)void SFTPD::handleDecryptedRequest(Request const &request){  xassert(controlChannelEncrypted());  switch (request.getCmd()) {    // --------------- 2228 commands -------------------------    case CMD_PBSZ: {      if (state != STATE_AUTHENTICATED) {        clientReply(RC_BAD_COMMAND_SEQUENCE,          "Can send PBSZ only once (for this server), "          "after authentication.");        break;      }      // parse the buffer length, represented as decimal integer      // as per RFC2228      {        unsigned long dataBufferSize =          f_ing_strtoul(request.getData(), 10 /*radix*/);        if (dataBufferSize < MIN_BUFFER_SIZE) {          // 2228 departure; the semantics of RC_PBSZ_TOO_SMALL are that          // the client must re-submit the PBSZ request          clientReply(            RC_PBSZ_TOO_SMALL,            stringb("PBSZ is too small; PBSZmin=" << (int)MIN_BUFFER_SIZE));          break;        }        // store the value, having passed policy tests        maxBlockSize = dataBufferSize;      }      // if it's too big, we just use and send back a smaller number      bool wasTooBig = false;      if (maxBlockSize > MAX_BUFFER_SIZE) {        maxBlockSize = MAX_BUFFER_SIZE;        wasTooBig = true;      }      // compute maximum length of cleartext to fill such a block      xassert(security);      maxCleartextBlockSize = security->        data().maximumCleartextSizeForBlock(maxBlockSize);          // maximum x, such that there is no size s <= x such that          // maximumEncodedSize(s) > maxBlockSize      // allocate buffer.. we're tacitly assuming that the encoded size      // is always at least as large as any of the intermediate forms      // (if this turns out to be wrong, then it's a flaw in either our      // design, or 2228, or both)      dataBuffer.setAllocated(mymax(maxBlockSize, maxCleartextBlockSize));      state = STATE_GOT_PBSZ;      if (!wasTooBig) {        clientReply(RC_COMMAND_OK, "The PBSZ is ok.");      }      else {        // client must parse this to see the "PBSZ="        clientReply(RC_COMMAND_OK,          stringb("PBSZ=" << maxBlockSize));      }      break;    }    case CMD_PROT: {      if (state != STATE_GOT_PBSZ) {        clientReply(RC_BAD_COMMAND_SEQUENCE,          "Can only send PROT after PBSZ.");        break;      }      if (!security) {        // not entirely sure what circumstances might provoke this,        // but it gets the security==NULL issue out of the following code        clientReply(RC_BAD_COMMAND_SEQUENCE,          "The current security mechanism does not support PROT.");        break;      }      // map the code into a protection level      char protCode = request.getData()[0];      if (protCode != 'C') {        DataSecurityLevel level =          security->data().getLevelForCode(protCode);        if (level == DSL_NONE) {          clientReply(RC_PARAMETER_SYNTAX_ERROR,            "Unknown PROT command code.");          break;          // 9/22/00 00:28 bugfix: 'break' was missing        }        // verify we can support it (should never fail, but now is as good        // as time as any to find out if there is a problem)        dataSecLevel = level;        xassert(dataSecLevel & security->data().getSupportedProtLevels());      }      else {        // cleartext is implemented directly in sftpd        dataSecLevel = DSL_CLEAR;      }      clientReply(RC_COMMAND_OK,        stringb("Data channel protection set to: " <<                getDSLString(dataSecLevel)));      break;    }    case CMD_AUTH:    case CMD_ADAT:      clientReply(RC_BAD_COMMAND_SEQUENCE,        "Cannot send AUTH nor ADAT after authentication (for this server).");      break;    case CMD_CCC:      clientReply(RC_REQUEST_DENIED,        "CCC not allowed by this server.");      break;    case CMD_MIC:    case CMD_CONF:    case CMD_ENC:      // this was *already* an encrypted request      clientReply(RC_BAD_COMMAND_SEQUENCE,        "Cannot nest MIC, CONF, nor ENC.");      break;    // ----------------- 959 commands ---------------------------    case CMD_PORT:      handlePortCommand(request);      break;    // data-channel commands    case CMD_RETR:   // read from server    case CMD_STOR:   // write to server    case CMD_STOU:   // write to server, but don't overwrite existing    case CMD_APPE:   // append to file on server    case CMD_NLST:   // names-only directory listing    case CMD_LIST:   // names + stats directory listing      // protocol semantics are that the incrementing is associated      // with the *attempt*, so at the earliest moment we see that      // the client sent a data-transmission command, we inc      if (security && dataChannelProtected()) {        if (requireDataEncryption) {                    // paranioa rule: never call newFile with anything other          // than DSL_PRIVATE when requireDataEncryption==true          security->data().newFile(DSL_PRIVATE);        }        else {          security->data().newFile(dataSecLevel);        }      }      // policy option; test this *after* newFile to obey file-number      // increment semantics      if (failsRequireEncTest()) {        break;      }      // If the client hasn't sent a PORT command, that means it is      // expecting to receive the connection on its default port, which      // is getRemotePort(client_control).  But it also means that we      // haven't taken the opportunity to tell the server where to contact      // sftpd.      if (!pasvMode() &&          doingDataRelay() &&          server_listen == INVALID_SOCKET) {        diagnostic("doing extra PORT for default-client-port transfer");        listenServerDataPort();      }      // relay command to server, and assuming it responds with 150 (which      // we will relay), expect one of:      //   (1) server will connect directly to the client, if      //       data channel protection is off, or      //   (2) server will open a connection to our listening      //       local socket (opened in previous PORT command),      //       in which case we will do data encryption and      //       relay at that time      //   (3) client will open a connection to our listening      //       local socket (opened in previous PASV command),      //   

⌨️ 快捷键说明

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