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

📄 sftpd.cpp

📁 伯克利做的SFTP安全文件传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
  // several parameters to 'writeToLog' together on a single output line)  char const *fmt = (level & LL_NO_LF)? "%s" : "%s\n";  // only UNIX has syslog, and separately encapsulating this nonportability  // doesn't seem to have much value# ifdef __UNIX__    if (useStdinStream && !stdoutLogAnyway) {      // log to syslog      // (this isn't a very good use of syslog's 'priority' parameter...      // somehow I need to rethink my overall logging strategy)      syslog(level & LL_DIAGNOSTIC? LOG_DEBUG : LOG_INFO, fmt, msg);      return;    }# endif // __UNIX__  // log to a file if syslog not avail or we're not running under inetd  if (logEventMask & LE_PRINT_TIMESTAMP) {    // write timestamp and process id -- since we're not using syslog    // we don't get this info for free    int month, day, year, hour, minute, second;    getCurrentDate(month, day, year);    getCurrentTime(hour, minute, second);    fprintf(logFile,            "[%d] %d/%d/%d %02d:%02d:%02d ",            getProcessId(),            month, day, year,            hour, minute, second);  }  // log the real message  fprintf(logFile, fmt, msg);  // flush the damned thing!  fflush(logFile);}STATICDEF void SFTPD::warnHandler(WarnLevel level, char const *message){  if (level & WARN_DEBUG) {    breaker();    // breakpoint  }  if (instance) {    instance->writeToLog(LL_LOG, LE_WARNING, message);  }  else {    // hmm.. this is certainly not good, but I'm not sure    // there's anything good to do about it    breaker();     // breakpoint, at least  }}void SFTPD::logSocketInfo(LoggableEvent event, SOCKET s){  sockaddr_in local, remote;  getSockName(s, local);  getSockPeer(s, remote);  log(event,    "  local:  " << formatAddress(local) << "\n" <<    "  remote: " << formatAddress(remote));      // no final '\n' because log() adds that}void SFTPD::clientReply(ReplyCode code, char const *msg){  clientReply(Reply(code, msg));}void SFTPD::clientReply(Reply const &reply){  if (controlChannelEncrypted()) {    encryptedClientReply(reply);  }  else {    unencryptedClientReply(reply);  }  // SHA replies during authentication  if (amAuthenticating()) {    // this line can be commented-out to deliberately return wrong    // digests, to test client's response, during development    digt.add(reply);  }}void SFTPD::send(Request const &req, SOCKET s){  diagnostic("request to ftpd: " << req.getTextNoPassword());  req.send(s);  addEntropy();}void SFTPD::send(Reply const &reply, SOCKET s){  diagnostic("reply to client: " << reply.getAllText());    // the reply already has a CRLF; this LF adds another    // blank line, which tends to separate things nicely  reply.send(s);  addEntropy();}// ------------------- request handling -----------------------------// substitute my port for the client'svoid SFTPD::portCommandRelay(Request const &request){  // record client port internally  if (!getClientPort(request.getData())) {    // error reply sent already    return;  }  // bind a socket locally for ftpd to contact eventually  listenServerDataPort();  // reply to client  clientReply(RC_COMMAND_OK, "PORT command ok.");}// relay a port command unchanged; watch for reflexivity testvoid SFTPD::portCommandStraightRelay(Request const &request){  // send the request to ftpd  send(request, server_control);  // get reply  Reply reply(*serverControlStream);  addEntropy();  diagnostic("(in pCSR) reply from ftpd: " << reply.getAllText());  if (first(reply.getCode()) != RCF_POSITIVE_COMPLETION) {    // let's assume this is caused by reflexivity test; switch    // to indirect mode, issue the relayed port command; throws    // an exception if that fails too    diagnostic("switching to indirect mode");    forceDataRelay = true;    portCommandRelay(request);  }  else {    // it worked; no big deal    diagnostic("port command was accepted");    // 9/22/00 02:01 bugfix: wasn't resetting this    serverPassivePort = NONPASV_PORT;    // but must send ftpd's reply!  this was a bug in 1.10 ... :(    clientReply(reply);  }}// this fn is called in both 959 interop and normal SafeTP modevoid SFTPD::handlePortCommand(Request const &request){  if (failsRequireEncTest()) {    return;  }  if (doingDataRelay()) {    // bugfix: clear any prior data-channel socket state    closeDataSockets();    // setup our own private listen port, forward that to ftpd,    // get its response, and say "ok" to client    portCommandRelay(request);  }  else {    // send the port command straight through, but watch for an    // error, since we expect certain ftpd's to not like it    portCommandStraightRelay(request);  }}// the idea is to allow anon connections to be handled entirely// by the normal ftpd, so it can do things like logging and// admission control correctlyvoid SFTPD::possiblyDoAnonExecDropdown(SOCKET whereToSendGreeting){  #ifdef __UNIX__    string userCmd = peekLine(client_control);    if (0==missing_stricmp(string(userCmd, 5), "USER ") &&        isAnonLogin(userCmd + 5)) {      // we want to hand things over to the anon ftp      // before that, though, we've got to shut down the ftpd we      // already started; that's done by closing sockets to it      close_socket(server_control);      // first, when the anon ftpd gets control, it's going to immediately      // send a 220, and we need to protect that from messing up the      // client; we do this by sending the 331 the client is expecting, but      // NOT sending CRLF, so ftpd's 220 will just be tacked on.      // then, ftpd will see the "USER ftp", because we left it in the      // buffer      // finally, it will respond with a 331.  to avoid *that* being seen      // as an additional response, we put a dash after the first 331, so      // it just looks like a multiline reply.      // note that this *will* work even if ftpd sends a multi-line      // intro, because RFC 959 specifies that the first line and last      // line must have the same reply code (which these do; that's ok),      // and that the intermediate lines either start with the same code      // or are indented *or* have a dash after the code -- a multi-line      // 220 would have intermediate lines that start with 220 but have the      // dash      sendAllString(whereToSendGreeting,                    "331-Guest login; handing control to ftpd; its intro: ");      // now, since we didn't take the USER command out of the buffer, ftpd      // should inherit it      // exec the ftpd      // "-l" (ell) is wu-ftpd's logging switch      execlp(anonFtpdBinary, anonFtpdBinary, "-l", NULL);      // only executed if execlp failed      xsyserror("execlp");    }    // if we didn't see an anon login, drop back into the normal loop    // (among other things, this means that the user only has one chance    // to trigger this response; but not likely a problem since mostly    // people have ftp clients that do it automatically)  #else     // windows    // mainly because we don't have 'exec' under windows..    xfailure("The -e switch is only supported with the Unix SafeTP server");  #endif}// handle a request as it came across the wirevoid SFTPD::handleOriginalRequest(Request const &request){  if (state == STATE_959_INTEROP) {    // forced-relay option    if (request.getCmd() == CMD_PORT) {      handlePortCommand(request);    }    else {      // just emulating 959; relay to server      send(request, server_control);    }    return;  }  // we SHA requests during authentication (this will also  // SHA a 959 USER or ACCT, but that won't matter)  if (amAuthenticating()) {    // SHA this request    digt.add(request);  }  switch (request.getCmd()) {    case CMD_AUTH: {      #if 0      // work in progress      if (strchr(request.getData(), '@')) {        // special behavior for relay service:        // pass it unmodified, and unnoticed        send(request, server_control);        break;      }      #endif // 0      if (state != STATE_UNAUTHENTICATED) {        // main reason we prevent this is it's unlikely to be        // used, and to implement it we'd have to reset ftpd ourselves        clientReply(RC_BAD_COMMAND_SEQUENCE,     // was parameter-not-implemented, I think bad command sequence is better                    "Can only AUTH once.");        break;      }      // an AUTH command clears any pre-existing authentication      // or authorization (above restriction notwithstanding)      if (security) {        delete security;      }      security = NULL;      // prevent use of x-cleartext      char const *methodName = request.getData();      bool skipQuery = false;      if (!allowCleartext &&          (0==missing_stricmp(methodName, "X-Cleartext") ||           0==missing_stricmp(methodName, "X-Cleartext2"))) {        skipQuery = true;        security = NULL;      }      // kerberos compatibility      if (0==missing_stricmp(methodName, "GSSAPI")) {        log(LE_HANDOFF, "client requested GSSAPI");        if (kerberosFtpdBinary.isempty()) {          /* Quote from RFC 2228:              If the server does not understand the named security mechanism, it              should respond with reply code 504.              If the server is not willing to accept the named security              mechanism, it should respond with reply code 534.          */          log(LE_HANDOFF, "but Kerberos compatibility is turned off; see -K switch");          clientReply(RC_REQUEST_DENIED /*534*/,            "Kerberos compatibility is turned off.  If you'd like to use "            "Kerberos with this server, contact the system administrator.");          break;        }                #ifdef __UNIX__          // send an affirmative reply (since that's where in the protocol          // kftpd will expect things to be)          clientReply(RC_FIRST_ADAT /*334*/,            "Ah, you want Kerberos.  I'll go get him (it?)... (send security data)");          // NOTE: this won't work unless we are using stdin/stdout as our          //       sockets.          // try to exec the kerberos binary with the special switch hacked          // into its sources.. assumes there are no *other* arguments that          // need to be passed (what if there are..?)          // since the main argument of relevance is -a, which prevents          // kftpd from accepting unencrypted connections, let's just supply          // it always          execl(kerberosFtpdBinary, kerberosFtpdBinary, "-S", "-a", NULL);          // if execl returns, it failed          string reason =            stringb("exec'ing " << kerberosFtpdBinary << " failed: " <<                    strerror(errno));          log(LE_HANDOFF, reason);          clientReply(RC_INTERNAL_ERROR, reason);    // protocol violation.. who cares        #else     // windows, etc.          clientReply(RC_REQUEST_DENIED,                      "Kerberos compatibility is only implemented for unix servers.  Sorry.");        #endif        break;      }      // see if we know about this mechanism      if (!skipQuery) {        // decide which local address to claim        IPAddress localToUse = getMyIPAsClientSeesIt();        // create the security provider        security = SecurityProvider::          findSecurityMechanism(methodName, NULL /*i.e., am server*/,                                localToUse,                                getRemoteAddress(client_control));      }      if (!security) {        // unknown security mechanism        log(LE_HANDOFF,             "client requested unknown security mechanism " << request.getData());        clientReply(RC_PARAMETER_NOT_IMPLEMENTED,          stringb("I don't know about mechanism \"" <<                  request.getData() <<                  "\".  I know about " <<                  (allowCleartext? "ILST" : "LIST") <<                  "=X-SafeTP1"));            // ILST ('incomplete list') if we accept X-Cleartext{2,}        break;      }      // indeed we do.. enter ADAT negotiation      composeAdatReply(true /*justAuthed*/);      break;    }    case CMD_ADAT: {      if (state != STATE_ADAT) {        clientReply(RC_BAD_COMMAND_SEQUENCE,          "Must send ADAT only after AUTH.");        break;      }      // ensure 'security' is expecting it      xassert(security);      xassert(security->control().expectingIncomingAdat());      // un-64 the data      DataBlock adat;      base64decode(adat, request.getData());      if (printAdats) {        adat.print("client adat to server");      }      // pass it to 'security'      security->control().incomingAdat(adat);      // ask 'security' what to do next      composeAdatReply(false /*justAuthed*/);      break;    }

⌨️ 快捷键说明

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