ftpd.c
来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,298 行 · 第 1/2 页
C
1,298 行
#ifndef lintstatic char *sccsid = "@(#)ftpd.c 4.3 (ULTRIX) 9/4/90";#endif lint/************************************************************************ * * * Copyright (c) 1984,1988,1989 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************//* * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. *//*#ifndef lintchar copyright[] ="Copyright (c) 1985 Regents of the University of California.\n\ All rights reserved.\n";#endif not lint#ifndef lintstatic char sccsid[] = "ftpd.c 5.10 (Berkeley) 9/4/87";#endif not lint*//* * Modification History: * * 07-Aug-90 dlong * Generate better audit records and allow more parameters to * reply(). * * 29-Mar-90 dlong * Get kerberos ticket. * * 15-Aug-89 dlong * Support enhanced security when logging in. * * 05-Nov-88 jsd * Use sgetpwnam() to avoid security hole * * 09-Jun-88 Mark Parenti * Changed signal handlers to void. * *//* * FTP server. */#include <sys/param.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/file.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/ftp.h>#include <arpa/inet.h>#include <arpa/telnet.h>#include <varargs.h>#include <stdio.h>#include <signal.h>#include <pwd.h>#include <setjmp.h>#include <netdb.h>#include <errno.h>#include <strings.h>#include <syslog.h>#include <sys/svcinfo.h>#include <sys/audit.h>#include <auth.h>#ifdef AUTHEN#include <krb.h>#endif AUTHEN/* * File containing login names * NOT to be used on this machine. * Commonly used to disallow uucp. */#define FTPUSERS "/etc/ftpusers"extern int errno;extern char *sys_errlist[];extern char *crypt();extern char version[];extern char *home; /* pointer to home directory for glob */extern FILE *popen(), *fopen(), *freopen();extern int pclose(), fclose();extern char *getline();extern char cbuf[];struct sockaddr_in ctrl_addr;struct sockaddr_in data_source;struct sockaddr_in data_dest;struct sockaddr_in his_addr;int data;jmp_buf errcatch, urgcatch;int logged_in;struct passwd *pw;int debug;int timeout = 900; /* timeout after 15 minutes of inactivity */int logging;int guest;int wtmp;int type;int form;int stru; /* avoid C keyword */int mode;int usedefault = 1; /* for data transfers */int pdata; /* for passive mode */int unique;int transflag;char tmpline[7];char hostname[32];char remotehost[32];/* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */#define SWAITMAX 90 /* wait at most 90 seconds */#define SWAITINT 5 /* interval between retries */int swaitmax = SWAITMAX;int swaitint = SWAITINT;void lostconn();void myoob();FILE *getdatasock(), *dataconn();main(argc, argv) int argc; char *argv[];{ int addrlen, on = 1; long pgid; char *cp;#ifdef AUTHEN int i; char namebuf[ANAME_SZ]; char *ptr; struct svcinfo *svcp;#endif AUTHEN addrlen = sizeof (his_addr); if (getpeername(0, &his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } addrlen = sizeof (ctrl_addr); if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); debug = 0;#ifdef 43BSD openlog("ftpd", LOG_PID, LOG_DAEMON);#else openlog("ftpd", LOG_PID);#endif 43BSD argc--, argv++; while (argc > 0 && *argv[0] == '-') { for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { case 'v': debug = 1; break; case 'd': debug = 1; break; case 'l': logging = 1; break; case 't': timeout = atoi(++cp); goto nextopt; break; default: fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", *cp); break; }nextopt: argc--, argv++; }/* * Fire up kerberos */#ifdef AUTHEN if((svcp = getsvc()) == NULL) { fputs("Cannot access security type\n", stderr); } if(svcp->svcauth.seclevel >= SEC_UPGRADE) { for (i = 0 ; svcp->svcpath[SVC_AUTH][i] != SVC_LAST; i++) if (svcp->svcpath[SVC_AUTH][i] == SVC_BIND) { if(gethostname(namebuf, sizeof(namebuf)) == -1) { fputs("gethostname failure\n", stderr); } if((ptr = index(namebuf, '.')) != (char *)0) *ptr = '\0'; if(krb_svc_init("hesiod", namebuf, (char *)NULL, 0, (char *)NULL, "/var/dss/kerberos/tkt/tkt.ftpd") != RET_OK) { fputs("Kerberos initialization failure\n", stderr); } } }#endif AUTHEN (void) freopen("/dev/null", "w", stderr); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if (signal(SIGURG, myoob) < 0) { syslog(LOG_ERR, "signal: %m"); } /* handle urgent data inline */#ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt: %m"); }#endif SO_OOBINLINE pgid = getpid(); if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { syslog(LOG_ERR, "ioctl: %m"); } dolog(&his_addr); /* do telnet option negotiation here */ /* * Set up default state */ logged_in = 0; data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; (void) gethostname(hostname, sizeof (hostname)); reply(220, "%s FTP server (%s) ready.", hostname, version); for (;;) { (void) setjmp(errcatch); (void) yyparse(); }}voidlostconn(){ if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(-1);}/* * Helper function for sgetpwnam(). */char *sgetsave(s) char *s;{#ifdef notdef char *new = strdup(s);#else char *malloc(); char *new = malloc((unsigned) strlen(s) + 1);#endif if (new == NULL) { reply(553, "Local resource failure"); dologout(1); }#ifndef notdef (void) strcpy(new, s);#endif return (new);}/* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */struct passwd *sgetpwnam(name) char *name;{ static struct passwd save; register struct passwd *p; char *sgetsave(); if ((p = getpwnam(name)) == NULL) return (p); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_comment); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_comment = sgetsave(p->pw_comment); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save);}static char aud_msg[BUFSIZ];pass(passwd) char *passwd;{ AUTHORIZATION *auth=NULL, *getauthuid(); char *xpasswd; extern int sec_level; char tmask[AUD_NPARAM+1]; char *ar[AUD_NPARAM]; int i=0; if (logged_in || pw == NULL) { reply(503, "Login with USER first."); return; } tmask[i] = T_LOGIN; ar[i++] = pw->pw_name; tmask[i] = T_HOMEDIR; ar[i++] = pw->pw_dir; tmask[i] = T_SERVICE; ar[i++] = "ftpd"; tmask[i] = T_HOSTADDR2; ar[i++] = (char *) his_addr.sin_addr; if (!guest) { /* "ftp" is only account allowed no password */ config_auth(); if(sec_level < SEC_UPGRADE) { xpasswd = crypt(passwd, pw->pw_passwd); /* The strcmp does not catch null passwords! */ if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { reply(530, "Login incorrect."); goto bad; } } else { if(!checkpass(pw->pw_uid, passwd) || ((auth=getauthuid(pw->pw_uid)) == NULL)) { reply(530, "Login incorrect."); goto bad; } } } setegid(pw->pw_gid); initgroups(pw->pw_name, pw->pw_gid); if (chdir(pw->pw_dir)) { reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; } /* grab wtmp before chroot */ wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); if (guest && chroot(pw->pw_dir) < 0) { reply(550, "Can't set guest privileges."); if (wtmp >= 0) { (void) close(wtmp); wtmp = -1; } goto bad; } if (!guest) reply(230, "User %s logged in.", pw->pw_name); else reply(230, "Guest login ok, access restrictions apply."); logged_in = 1; dologin(pw); if(auth) { if(audcntl(SET_PROC_ACNTL, (char *) 0, 0, auth->a_audit_control, 0) < 0) { reply(530, "audcntl error."); goto bad; } if(audcntl(SET_PROC_AMASK, auth->a_audit_mask, SYSCALL_MASK_LEN+TRUSTED_MASK_LEN, 0) < 0) { reply(530, "audit mask error."); goto bad; } if(audcntl(SETPAID, (char *) 0, 0, 0, auth->a_audit_id) < 0) { reply(530, "error setting audit ID."); goto bad; } } if(*aud_msg) { tmask[i] = T_CHARP; ar[i++] = aud_msg; } tmask[i] = T_RESULT; ar[i++] = (char *) 0; tmask[i] = '\0'; audgen(LOGIN, tmask, ar); seteuid(pw->pw_uid); home = pw->pw_dir; /* home dir for globbing */ return;bad: seteuid(0); if(*aud_msg) { tmask[i] = T_CHARP; ar[i++] = aud_msg; } tmask[i] = T_ERROR; ar[i++] = (char *) 1; tmask[i] = '\0'; audgen(LOGIN, tmask, ar); pw = NULL;}retrieve(cmd, name) char *cmd, *name;{ FILE *fin, *dout; struct stat st; int (*closefunc)(), tmp; if (cmd == 0) {#ifdef notdef /* no remote command execution -- it's a security hole */ if (*name == '|') fin = popen(name + 1, "r"), closefunc = pclose; else#endif fin = fopen(name, "r"), closefunc = fclose; } else { char line[BUFSIZ]; (void) sprintf(line, cmd, name), name = line; fin = popen(line, "r"), closefunc = pclose; } if (fin == NULL) { if (errno != 0) reply(550, "%s: %s.", name, sys_errlist[errno]); return; } st.st_size = 0; if (cmd == 0 && (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { reply(550, "%s: not a plain file.", name); goto done; } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { reply(550, "%s: %s.", name, sys_errlist[errno]); } else if (tmp == 0) { reply(226, "Transfer complete."); } (void) fclose(dout); data = -1; pdata = -1;done: (*closefunc)(fin);}store(name, mode) char *name, *mode;{ FILE *fout, *din; int (*closefunc)(), dochown = 0, tmp; char *gunique(), *local;#ifdef notdef /* no remote command execution -- it's a security hole */ if (name[0] == '|') fout = popen(&name[1], "w"), closefunc = pclose; else#endif { struct stat st; local = name; if (stat(name, &st) < 0) { dochown++; } else if (unique) { if ((local = gunique(name)) == NULL) { return; } dochown++; } fout = fopen(local, mode), closefunc = fclose; } if (fout == NULL) { reply(553, "%s: %s.", local, sys_errlist[errno]); return; } din = dataconn(local, (off_t)-1, "r"); if (din == NULL) goto done; if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { reply(552, "%s: %s.", local, sys_errlist[errno]); } else if (tmp == 0 && !unique) { reply(226, "Transfer complete."); } else if (tmp == 0 && unique) { reply(226, "Transfer complete (unique file name:%s).", local); } (void) fclose(din); data = -1; pdata = -1;done: if (dochown) (void) chown(local, pw->pw_uid, -1); (*closefunc)(fout);}FILE *getdatasock(mode) char *mode;{ int s, on = 1; if (data >= 0) return (fdopen(data, mode)); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return (NULL); seteuid(0); if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ data_source.sin_family = AF_INET; data_source.sin_addr = ctrl_addr.sin_addr; if (bind(s, &data_source, sizeof (data_source)) < 0) goto bad; seteuid(pw->pw_uid); return (fdopen(s, mode));bad: seteuid(pw->pw_uid); (void) close(s); return (NULL);}FILE *dataconn(name, size, mode) char *name; off_t size; char *mode;{ char sizebuf[32]; FILE *file; int retry = 0; if (size >= 0) (void) sprintf (sizebuf, " (%ld bytes)", size); else (void) strcpy(sizebuf, ""); if (pdata > 0) { struct sockaddr_in from; int s, fromlen = sizeof(from); s = accept(pdata, &from, &fromlen); if (s < 0) { reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return(NULL); } (void) close(pdata); pdata = s; reply(150, "Openning data connection for %s (%s,%d)%s.", name, inet_ntoa(from.sin_addr), ntohs(from.sin_port), sizebuf); return(fdopen(pdata, mode)); } if (data >= 0) { reply(125, "Using existing data connection for %s%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; file = getdatasock(mode); if (file == NULL) { reply(425, "Can't create data socket (%s,%d): %s.", inet_ntoa(data_source.sin_addr), ntohs(data_source.sin_port), sys_errlist[errno]); return (NULL); } data = fileno(file); while (connect(data, &data_dest, sizeof (data_dest)) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; continue; } reply(425, "Can't build data connection: %s.", sys_errlist[errno]); (void) fclose(file); data = -1; return (NULL); } reply(150, "Opening data connection for %s (%s,%d)%s.", name, inet_ntoa(data_dest.sin_addr), ntohs(data_dest.sin_port), sizebuf); return (file);}/* * Tranfer the contents of "instr" to * "outstr" peer using the appropriate
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?