📄 sftpd.cpp
字号:
case 'o': stdoutLogAnyway = true; break; case 'd': // print diagnostics logLevelMask = (LogLevel)(logLevelMask | LL_DIAGNOSTIC); if (isdigit(argv[a][2])) { int d = argv[a][2] - '0'; if (d >= 2) { logLevelMask = (LogLevel)(logLevelMask | LL_SOCKET_DIAG); } if (d >= 3) { printDataChannel = true; } } break; case 'a': // print ADATs printAdats = true; break; case 'l': { // set log file FILE *fp = fopen(argString, "a"); if (!fp) { log(LE_ERROR, "failed to open log file " << argString); errors = true; return; } else { if (logFile != stderr) { fclose(logFile); } logFile = fp; // it's silly to require that people specify -o with -l stdoutLogAnyway = true; } break; } case 'm': logEventMask = (LoggableEvent) f_ing_strtoul(argv[a]+2, 0 /*auto-radix*/); break; case 'x': if (requireDataEncryption) { // handle -c before -x log(LE_ERROR, "-x is incompatible with -c"); errors = true; } else { allowCleartext = true; } break; case '9': allowRfc959 = false; allowRfc959_anon = false; break; case '8': // like above, but we do allow dropdown for anon allowRfc959 = false; allowRfc959_anon = true; break; case 't': waitForAttach(); break; case 'y': if (!changeDirectory(argString)) { log(LE_ERROR, "failed to chdir to " << argString); errors = true; } break; case 'K': kerberosFtpdBinary = argString; break; case 'e': anonExecDropdown = true; anonFtpdBinary = argString; break; case 'i': // send a different address when authenticating; // for a server sitting behind an address-translating firewall fakeIPAddress = resolveHostName(argString); useFakeIPAddress = true; break; case 'c': requireDataEncryption = true; // 9/22/00 02:07: tightening-down -c even more: make it // imply -3, so the direct-to-ftpd route is completely // closed, and -9 so we never accept unencrypted conns forceDataRelay = true; allowRfc959 = false; allowRfc959_anon = false; allowCleartext = false; // in case -c after -x break; case 'r': case 'R': { // parse low/high StrtokParse tok(argString, "-"); if (tok != 2) { log(LE_ERROR, "argument to -" << argv[a][1] << " is malformed"); errors = true; break; } int low = atoi(tok[0]); int high = atoi(tok[1]); // may as well sanity-check the values; don't let them // be 0 because that is atoi's error return if (!( 1 <= low && low <= 0xFFFF && 1 <= high && high <= 0xFFFF && low <= high )) { log(LE_ERROR, "argument(s) to -" << argv[a][1] << " malformed or out of range [1,65535]"); errors = true; break; } // stash the range in the proper variable if (argv[a][1] == 'r') { pasvRange.setRange(low, high); } else { activeRange.setRange(low, high); } break; } case 'h': // purpose of defining this is to ensure we reserve // an option for printing the help text // (dropthru to next case) default: oneError = true; break; } } if (oneError) { // "-h" is treated like an error in most respects, // but shouldn't generate an actual error message if (!( argv[a][0] == '-' && argv[a][1] == 'h' )) { // potential problem here: errors in inetd.conf will // not cause visible error messages... //fprintf(stderr, "error in argument: %s\n", argv[a]); // possibly addressed; if the log output file is specified // early, then the user will see later errors log(LE_ERROR, "error in argument: " << argv[a]); } // cause usage to be printed and program to exit errors = true; } } // see if we had any trouble before processing command line if (delayedError) { xSysError temp(*delayedError); delete delayedError; // such a good little soldier! (Dan was making fun of me for this :) ) THROW(temp); } // print usage if any argument errors if (errors) { fprintf(stderr, "usage: sftpd [options]\n" " general options:\n" " -v print version\n" " -h print this message\n" " -y<directory> specify directory where keys are (default is cwd)\n" " control channel:\n" " -s use stdin as control connection (for use with inetd)\n" " -p<N> listen for incoming connections on port N (default: %d)\n" " -p<ADDR>:<N> listen on interface ADDR (dotted decimal) and port N\n" " -f<N> contact ftpd on port N (default: %d)\n" " -f<ADDR>:<N> contact ftpd at another host (ADDR) and port N;\n" " (this is potentially INSECURE -- see sftpd.html)\n" " data channel:\n" " -c require all data transfers to be encrypted\n" " -r<low>-<high> restrict ports used for PASV to range [low,high]\n" " -R<low>-<high> restrict ports used for PORT to range [low,high]\n" " logging:\n" " -l<filename> log to a file instead of stdout or syslog\n" " -o for -s, send log output to stdout instead of syslog\n" " -d1 log diagnostic (debugging) messages\n" " -d2 .. log even more detailed diagnostic messages\n" " -d3 .. and print all data channel traffic to stdout\n" " -a log ADATs\n" " -m<mask> set mask of events to log -- see sftpd.html\n" " compatibility:\n" " -9 disallow RFC 959 (unencrypted) connections\n" " -8 disallow RFC 959 unless the user is anonymous\n" " -3 disable 3rd-party transfer optimization\n" " -i<address> use alternate server address for authenication\n" " -K<filename> Kerberos compatibility (specify kftpd image)\n" " -e<filename> Handle anon ftp by exec'ing this ftpd to handle it\n" //" -x allow X-Cleartext and X-Cleartext2 protocols\n" //" -t start and immediately wait for debugger to attach\n" //" -Dn sleep n seconds between data blocks (simulates slow link)\n" " for inetd, typical arguments are -s -y/home/safetp -f351\n", sftpdPort, ftpdPort); return; } } if (anonExecDropdown && !useStdinStream) { log(LE_ERROR, "error: can only use the -e switch when run from inetd (-s switch)"); // this is because the exec'd ftpd will expect sockets on stdin and // stdout, but if we're not run from inetd, then stdin and stdout // won't even be sockets, let alone the right ones return; } if (ftpdPort == sftpdPort && !ftpdForwardNonlocally) { log(LE_WARNING, "warning: sftpd may get into an infinite loop; is " << ftpdPort << " the right port to contact? (this warning " "is triggered by ftpdPort==sftpdPort)"); } if (useStdinStream) { // we don't want throwing exceptions to write to stderr because // that is now connected to the control channel (could redirect // it, but we probably don't want the exception-thrown records // in the log file at all at this stage of development) xBase::logExceptions = false; } if (logLevelMask & LL_DIAGNOSTIC) { // turn on digest echoes DigestComputer::echoDigestInput = true; } // read seed; we wait until now because we don't want to risk // generating log output until the -s option, if present, has // been processed readRandomSeed(); // and now, we want to make sure we will write the updated version, // to avoid replay potential RandomSeedSaver seedSaver(this); // Dan and I both ran into situations where it would have been nice // for sftpd to report missing keys earlier.. so let's try to do that if (!sm_testKey("DSA/public.key", true) || !sm_testKey("DSA/private.key", true)) { log(LE_ERROR, "ERROR: at least one of the DSA keys is missing"); // even though in theory there is something useful sftpd can do // without its keys (namely, test X-CLEARTEXT), that's a very rare // case; to avoid confusion, bail return; } // ----------- listen for a new connection -------------- if (useStdinStream) { // we are already connected client_control = theStdin; // file descriptor 0 is stdin; note that INVALID_SOCKET is -1, // so we aren't going to collide with the error value incOpenSockets(); // since we inherited this socket, rather than opened in directly, // we have to increment this manually } else { // NOTE: we must run as root here, if sftpdPort is < 1024 // establish a listener on the sftpd port SOCKET listener = interface_listen_socket(sftpdInterface, sftpdPort); addEntropy(); // wait for a connection if (sftpdInterface == INADDR_ANY) { log(LE_CONTROL_CHANNEL, "Waiting for connection to sftpd on port " << sftpdPort << "..."); } else { log(LE_CONTROL_CHANNEL, "Waiting for connection to sftpd on interface " << formatAddress(sftpdInterface, sftpdPort) << "..."); } client_control = accept_socket(listener); addEntropy(); // don't need listener any longer closeSocket(listener); // TODO: some people do run sftpd as root; it would be good to // drop root privileges here } // begin: exceptions should cause a protocol message try { // set the default client data transfer port relayClientAddress = getRemoteAddress(client_control); relayClientPort = getRemotePort(client_control); // print connection information log(LE_CONTROL_CHANNEL, "received connection from host " << formatAddress(relayClientAddress, relayClientPort)); // ----------- connect to ftpd -------------- // it appears that typical ftpd implementations do not allow // arbitrary PORT command IP addresses when their control // connection is to localhost (127.0.0.1); therefore, we find out // which IP address the client connected to, and use that IP when // connecting to ftpd (unless an alternate has been supplied with // -f); this is necessary only when data channel protection is // *off*, because in that situation we let the client's PORT // commands through unmodified // decide which address to use for contacting ftpd if (!ftpdForwardNonlocally) { // use same interface that client connected to ftpdAddress = getLocalAddress(client_control); } else { // user has already specified it, and it is stored // in 'ftpdAddress' } // open connection to real ftp server diagnostic("connecting to ftpd at " << formatAddress(ftpdAddress, ftpdPort) << "..."); server_control = connect_socket(ftpdAddress, ftpdPort); addEntropy(); diagnostic("connected to ftpd"); // display socket info socket_diagnostic("Control connection to client: " << sockInfo(client_control)); socket_diagnostic("Control connection to ftpd: " << sockInfo(server_control)); // ----------- initial relay -------------- // construct buffering layer on server socket serverControlStream = new SocketLineReader(server_control); // annotate initial response from the server bool readReplyOk = false; try { Reply reply(*serverControlStream); readReplyOk = true; addEntropy(); reply.append("*** This server can accept secure (encrypted) connections. ***"); reply.append("*** See http://safetp.cs.berkeley.edu for info. ***"); send(reply, client_control); } catch (xBase &) { if (!readReplyOk) { // the most common cause is the exception reported by reply.cpp about reading // past the end of the stream, caused by the server accepting the connection // but immediately closing it without sending a greeting xfailure("FTP server immediately closed connection; it's probably disabled"); } else { // something else; handle normally throw; } } // new feature: anon dropdown via exec if (anonExecDropdown) { possiblyDoAnonExecDropdown(client_control); } // only *after* the possible anon dropdown stuff do we // setup buffering on the client's control channel clientControlStream = new SocketLineReader(client_control); // modify session state state = STATE_UNAUTHENTICATED; } catch (xBase &x) { // report error to user, via protocol stream unexpected(x); // the error has been adequately reported; just bail return; } // ----------- main response loop -------------- // enter request-reply loop for(;;) { // we'll record which one socket being used, for // processing during catch-all catch statement
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -