#include <u.h>#include <libc.h>#include <bio.h>#include <ip.h>#include <libsec.h>#include <auth.h> #include <String.h>#include "glob.h" enum { Tascii, Timage, Maxpath = 512, Maxwait = 1000 * 60 * 30, /* 30 minutes */}; typedef struct Passive Passive;typedef struct Ftpd Ftpd;typedef struct Cmd Cmd; struct Passive { int inuse; char adir[40]; int afd; int port; uchar ipaddr[IPaddrlen];}; struct Ftpd { Biobuf *in, *out; struct conn { int tlson, tlsondata; NetConnInfo *nci; TLSconn *tls; uchar *cert; int certlen; char data[64]; Passive pasv; } conn; struct user { char cwd[Maxpath]; char name[Maxpath]; int loggedin; int isnone; } user; int type; vlong offset; int cmdpid; char *renamefrom;}; struct Cmd { char *name; int (*fn)(Ftpd *, char *); int needlogin; int needtls; int asproc;}; char *certpath;char *namespace = "/lib/namespace.ftp";int implicittls;int debug;int anonok;int anononly;int anonall; void dprint(char *fmt, ...){ char *msg; va_list arg; if(!debug) return; va_start(arg, fmt); msg = vsmprint(fmt, arg); va_end(arg); syslog(0, "ftp", msg); free(msg);} void logit(char *fmt, ...){ char *msg; va_list arg; va_start(arg, fmt); msg = vsmprint(fmt, arg); va_end(arg); syslog(0, "ftp", msg); free(msg);} int reply(Biobuf *bio, char *fmt, ...){ va_list arg; char buf[Maxpath], *s; va_start(arg, fmt); s = vseprint(buf, buf + sizeof(buf) - 3, fmt, arg); va_end(arg); dprint("rpl: %s", buf); *s++ = '\r'; *s++ = '\n'; Bwrite(bio, buf, s - buf); Bflush(bio); return 0;} voidasproc(Ftpd *ftpd, int (*f)(Ftpd *, char *), char *arg){ int i; if(ftpd->cmdpid) { for(;;) { i = waitpid(); if(i == ftpd->cmdpid || i < 0) break; } } switch(ftpd->cmdpid = rfork(RFFDG|RFPROC|RFNOTEG)){ case -1: reply(ftpd->out, "450 Out of processes: %r"); return; case 0: (*f)(ftpd, arg); dprint("proc exiting"); exits(nil); default: break; }} int mountnet(Ftpd *ftpd){ if(bind("#/", "/", MAFTER) == -1) { reply(ftpd->out, "500 can't bind #/ to /: %r"); return -1; } if(bind(ftpd->conn.nci->spec, "/net", MBEFORE) == -1) { reply(ftpd->out, "500 can't bind %s to /net: %r", ftpd->conn.nci->spec); unmount("#/", "/"); return -1; } return 0;} void unmountnet(void){ unmount(nil, "/net"); unmount("#/", "/");} Biobuf *dialdata(Ftpd *ftpd, int read){ Biobuf *bio; TLSconn *tls; int fd, cfd; char ldir[40]; if(mountnet(ftpd) < 0) return nil; if(!ftpd->conn.pasv.inuse) { fd = dial(ftpd->conn.data, "20", 0, 0); } else { fd = -1; alarm(30 * 1000); /* wait 30 seconds */ dprint("dbg: waiting for passive connection"); cfd = listen(ftpd->conn.pasv.adir, ldir); alarm(0); if(cfd >= 0) { fd = accept(cfd, ldir); close(cfd); } } if(fd < 0) { reply(ftpd->out, "425 Error opening data connection"); unmountnet(); return nil; } reply(ftpd->out, "150 Opened data connection"); tls = nil; if(ftpd->conn.tlsondata) { dprint("dbg: using tls on data channel"); tls = mallocz(sizeof(TLSconn), 1); tls->cert = malloc(ftpd->conn.certlen); memcpy(tls->cert, ftpd->conn.cert, ftpd->conn.certlen); tls->certlen = ftpd->conn.certlen; fd = tlsServer(fd, tls); if(fd < 0) { reply(ftpd->out, "425 TLS on data connection failed"); unmountnet(); return nil; } dprint("dbg: tlsserver done"); } unmountnet(); if(read) bio = Bfdopen(fd, OREAD); else bio = Bfdopen(fd, OWRITE); bio->aux = tls; return bio;} void closedata(Ftpd *ftpd, Biobuf *bio, int fail){ TLSconn *conn; conn = bio->aux; Bflush(bio); Bterm(bio); if(!fail) reply(ftpd->out, "226 Transfer complete"); if(conn) { free(conn->cert); free(conn); }} int starttls(Ftpd *ftpd){ int fd; fd = tlsServer(0, ftpd->conn.tls); if(fd < 0) return -1; dup(fd, 0); dup(fd, 1); ftpd->conn.tlson = 1; return 0;} intabortcmd(Ftpd *ftpd, char *arg){ USED(arg); if(ftpd->cmdpid){ if(postnote(PNPROC, ftpd->cmdpid, "kill") == 0) reply(ftpd->out, "426 Command aborted"); else logit("postnote pid %d %r", ftpd->cmdpid); } return reply(ftpd->out, "226 Abort processed");} int authcmd(Ftpd *ftpd, char *arg){ if((cistrcmp(arg, "TLS") == 0) || (cistrcmp(arg, "TLS-C") == 0) || (cistrcmp(arg, "SSL") == 0)) { if(!ftpd->conn.tls) return reply(ftpd->out, "431 tls not enabled"); reply(ftpd->out, "234 starting tls"); if(starttls(ftpd) < 0) return reply(ftpd->out, "431 tls failed"); } else { return reply(ftpd->out, "502 security method %s not understood", arg); } return 0;} int cwdcmd(Ftpd *ftpd, char *arg){ char buf[Maxpath]; if(!arg || *arg == '\0') { if(ftpd->user.isnone) snprint(buf, Maxpath, "/"); else snprint(buf, Maxpath, "/usr/%s", ftpd->user.name); } else { strncpy(buf, arg, Maxpath); cleanname(buf); } if(chdir(buf) < 0) return reply(ftpd->out, "550 CWD failed: %r"); getwd(ftpd->user.cwd, Maxpath); return reply(ftpd->out, "200 Directory changed to %s", ftpd->user.cwd);} int deletecmd(Ftpd *ftpd, char *arg){ if(!arg) return reply(ftpd->out, "501 Rmdir/Delete command needs an argument"); if(ftpd->user.isnone) return reply(ftpd->out, "550 Permission denied"); if(remove(cleanname(arg)) < 0) return reply(ftpd->out, "550 Can't remove %s: %r", arg); else return reply(ftpd->out, "226 \"%s\" removed", arg);} int featcmd(Ftpd *ftpd, char *arg){ USED(arg); reply(ftpd->out, "211-Features supported"); reply(ftpd->out, " UTF8"); reply(ftpd->out, " PBSZ"); reply(ftpd->out, " PROT"); reply(ftpd->out, " AUTH TLS"); reply(ftpd->out, " MLST Type*;Size*;Modify*;Unix.groupname*;UNIX.ownername*;"); return reply(ftpd->out, "211 End");} int dircmp(void *va, void *vb){ Dir *a, *b; a = va; b = vb; return strcmp(a->name, b->name);} voidlistdir(Ftpd *ftpd, Biobuf *data, char *path, void (*fn)(Biobuf *, Dir *d, char *dirname)){ Dir *dirbuf; int fd; long ndirs; long i; fd = open(path, OREAD); if(!fd) return; ndirs = dirreadall(fd, &dirbuf); if(ndirs < 1) return; close(fd); qsort(dirbuf, ndirs, sizeof(Dir), dircmp); for(i=0;i<ndirs;i++) (*fn)(data, &dirbuf[i], (strcmp(path, ftpd->user.cwd) == 0 ? nil : path)); free(dirbuf);} intlist(Ftpd *ftpd, char *arg, void (*fn)(Biobuf *, Dir *d, char *dirname)){ Biobuf *data; int argc, i; char *argv[32]; Globlist *gl; char *path; Dir *d; if(arg) { argc = getfields(arg, argv, sizeof(argv)-1, 1, " \t"); } else { argc = 1; argv[0] = ftpd->user.cwd; } data = dialdata(ftpd, 0); if(!data) return reply(ftpd->out, "500 List failed: couldn't dial data"); for(i=0;i<argc;i++) { gl = glob(argv[i]); if(!gl) continue; while(path = globiter(gl)) { cleanname(path); logit("list: path %s user %s", path, ftpd->user.name); d = dirstat(path); if(d->mode & DMDIR) listdir(ftpd, data, path, fn); else (*fn)(data, d, nil); free(d); } } closedata(ftpd, data, 0); return 0;} char *mode2asc(int m){ char *asc; char *p; asc = strdup("----------"); if(DMDIR & m) asc[0] = 'd'; if(DMAPPEND & m) asc[0] = 'a'; else if(DMEXCL & m) asc[3] = 'l'; for(p = asc + 1; p < asc + 10; p += 3, m <<= 3) { if(m & 0400) p[0] = 'r'; if(m & 0200) p[1] = 'w'; if(m & 0100) p[2] = 'x'; } return asc;} void listprint(Biobuf *data, Dir *d, char *dirname){ char *ts, *mode; ts = strdup(ctime(d->mtime)); ts[16] = '\0'; if(time(0) - d->mtime > 6 * 30 * 24 * 60 * 60) memmove(ts + 11, ts + 23, 5); mode = mode2asc(d->mode); if(dirname) reply(data, "%s %3d %-8s %-8s %7lld %s %s/%s", mode, 1, d->uid, d->gid, d->length, ts + 4, dirname, d->name); else reply(data, "%s %3d %-8s %-8s %7lld %s %s", mode, 1, d->uid, d->gid, d->length, ts + 4, d->name); free(mode); free(ts);} int listcmd(Ftpd *ftpd, char *arg){ return list(ftpd, arg, listprint);} int loginuser(Ftpd *ftpd, char *pass, char *nsfile){ char *user; user = ftpd->user.name; putenv("service", "ftp"); if(!ftpd->user.isnone) { if(login(user, pass, nsfile) < 0) return reply(ftpd->out, "530 Not logged in: bad password"); } else { if(newns(user, nsfile) < 0) return reply(ftpd->out, "530 Not logged in: user out of service"); }Lines 1–500 of 1124. Load more
#include <u.h> #include <libc.h> #include <bio.h> #include <ip.h> #include <libsec.h> #include <auth.h> #include <String.h> #include "glob.h" enum { Tascii, Timage, Maxpath = 512, Maxwait = 1000 * 60 * 30, /* 30 minutes */ }; typedef struct Passive Passive; typedef struct Ftpd Ftpd; typedef struct Cmd Cmd; struct Passive { int inuse; char adir[40]; int afd; int port; uchar ipaddr[IPaddrlen]; }; struct Ftpd { Biobuf *in, *out; struct conn { int tlson, tlsondata; NetConnInfo *nci; TLSconn *tls; uchar *cert; int certlen; char data[64]; Passive pasv; } conn; struct user { char cwd[Maxpath]; char name[Maxpath]; int loggedin; int isnone; } user; int type; vlong offset; int cmdpid; char *renamefrom; }; struct Cmd { char *name; int (*fn)(Ftpd *, char *); int needlogin; int needtls; int asproc; }; char *certpath; char *namespace = "/lib/namespace.ftp"; int implicittls; int debug; int anonok; int anononly; int anonall; void dprint(char *fmt, ...) { char *msg; va_list arg; if(!debug) return; va_start(arg, fmt); msg = vsmprint(fmt, arg); va_end(arg); syslog(0, "ftp", msg); free(msg); } void logit(char *fmt, ...) { char *msg; va_list arg; va_start(arg, fmt); msg = vsmprint(fmt, arg); va_end(arg); syslog(0, "ftp", msg); free(msg); } int reply(Biobuf *bio, char *fmt, ...) { va_list arg; char buf[Maxpath], *s; va_start(arg, fmt); s = vseprint(buf, buf + sizeof(buf) - 3, fmt, arg); va_end(arg); dprint("rpl: %s", buf); *s++ = '\r'; *s++ = '\n'; Bwrite(bio, buf, s - buf); Bflush(bio); return 0; } void asproc(Ftpd *ftpd, int (*f)(Ftpd *, char *), char *arg) { int i; if(ftpd->cmdpid) { for(;;) { i = waitpid(); if(i == ftpd->cmdpid || i < 0) break; } } switch(ftpd->cmdpid = rfork(RFFDG|RFPROC|RFNOTEG)){ case -1: reply(ftpd->out, "450 Out of processes: %r"); return; case 0: (*f)(ftpd, arg); dprint("proc exiting"); exits(nil); default: break; } } int mountnet(Ftpd *ftpd) { if(bind("#/", "/", MAFTER) == -1) { reply(ftpd->out, "500 can't bind #/ to /: %r"); return -1; } if(bind(ftpd->conn.nci->spec, "/net", MBEFORE) == -1) { reply(ftpd->out, "500 can't bind %s to /net: %r", ftpd->conn.nci->spec); unmount("#/", "/"); return -1; } return 0; } void unmountnet(void) { unmount(nil, "/net"); unmount("#/", "/"); } Biobuf * dialdata(Ftpd *ftpd, int read) { Biobuf *bio; TLSconn *tls; int fd, cfd; char ldir[40]; if(mountnet(ftpd) < 0) return nil; if(!ftpd->conn.pasv.inuse) { fd = dial(ftpd->conn.data, "20", 0, 0); } else { fd = -1; alarm(30 * 1000); /* wait 30 seconds */ dprint("dbg: waiting for passive connection"); cfd = listen(ftpd->conn.pasv.adir, ldir); alarm(0); if(cfd >= 0) { fd = accept(cfd, ldir); close(cfd); } } if(fd < 0) { reply(ftpd->out, "425 Error opening data connection"); unmountnet(); return nil; } reply(ftpd->out, "150 Opened data connection"); tls = nil; if(ftpd->conn.tlsondata) { dprint("dbg: using tls on data channel"); tls = mallocz(sizeof(TLSconn), 1); tls->cert = malloc(ftpd->conn.certlen); memcpy(tls->cert, ftpd->conn.cert, ftpd->conn.certlen); tls->certlen = ftpd->conn.certlen; fd = tlsServer(fd, tls); if(fd < 0) { reply(ftpd->out, "425 TLS on data connection failed"); unmountnet(); return nil; } dprint("dbg: tlsserver done"); } unmountnet(); if(read) bio = Bfdopen(fd, OREAD); else bio = Bfdopen(fd, OWRITE); bio->aux = tls; return bio; } void closedata(Ftpd *ftpd, Biobuf *bio, int fail) { TLSconn *conn; conn = bio->aux; Bflush(bio); Bterm(bio); if(!fail) reply(ftpd->out, "226 Transfer complete"); if(conn) { free(conn->cert); free(conn); } } int starttls(Ftpd *ftpd) { int fd; fd = tlsServer(0, ftpd->conn.tls); if(fd < 0) return -1; dup(fd, 0); dup(fd, 1); ftpd->conn.tlson = 1; return 0; } int abortcmd(Ftpd *ftpd, char *arg) { USED(arg); if(ftpd->cmdpid){ if(postnote(PNPROC, ftpd->cmdpid, "kill") == 0) reply(ftpd->out, "426 Command aborted"); else logit("postnote pid %d %r", ftpd->cmdpid); } return reply(ftpd->out, "226 Abort processed"); } int authcmd(Ftpd *ftpd, char *arg) { if((cistrcmp(arg, "TLS") == 0) || (cistrcmp(arg, "TLS-C") == 0) || (cistrcmp(arg, "SSL") == 0)) { if(!ftpd->conn.tls) return reply(ftpd->out, "431 tls not enabled"); reply(ftpd->out, "234 starting tls"); if(starttls(ftpd) < 0) return reply(ftpd->out, "431 tls failed"); } else { return reply(ftpd->out, "502 security method %s not understood", arg); } return 0; } int cwdcmd(Ftpd *ftpd, char *arg) { char buf[Maxpath]; if(!arg || *arg == '\0') { if(ftpd->user.isnone) snprint(buf, Maxpath, "/"); else snprint(buf, Maxpath, "/usr/%s", ftpd->user.name); } else { strncpy(buf, arg, Maxpath); cleanname(buf); } if(chdir(buf) < 0) return reply(ftpd->out, "550 CWD failed: %r"); getwd(ftpd->user.cwd, Maxpath); return reply(ftpd->out, "200 Directory changed to %s", ftpd->user.cwd); } int deletecmd(Ftpd *ftpd, char *arg) { if(!arg) return reply(ftpd->out, "501 Rmdir/Delete command needs an argument"); if(ftpd->user.isnone) return reply(ftpd->out, "550 Permission denied"); if(remove(cleanname(arg)) < 0) return reply(ftpd->out, "550 Can't remove %s: %r", arg); else return reply(ftpd->out, "226 \"%s\" removed", arg); } int featcmd(Ftpd *ftpd, char *arg) { USED(arg); reply(ftpd->out, "211-Features supported"); reply(ftpd->out, " UTF8"); reply(ftpd->out, " PBSZ"); reply(ftpd->out, " PROT"); reply(ftpd->out, " AUTH TLS"); reply(ftpd->out, " MLST Type*;Size*;Modify*;Unix.groupname*;UNIX.ownername*;"); return reply(ftpd->out, "211 End"); } int dircmp(void *va, void *vb) { Dir *a, *b; a = va; b = vb; return strcmp(a->name, b->name); } void listdir(Ftpd *ftpd, Biobuf *data, char *path, void (*fn)(Biobuf *, Dir *d, char *dirname)) { Dir *dirbuf; int fd; long ndirs; long i; fd = open(path, OREAD); if(!fd) return; ndirs = dirreadall(fd, &dirbuf); if(ndirs < 1) return; close(fd); qsort(dirbuf, ndirs, sizeof(Dir), dircmp); for(i=0;i<ndirs;i++) (*fn)(data, &dirbuf[i], (strcmp(path, ftpd->user.cwd) == 0 ? nil : path)); free(dirbuf); } int list(Ftpd *ftpd, char *arg, void (*fn)(Biobuf *, Dir *d, char *dirname)) { Biobuf *data; int argc, i; char *argv[32]; Globlist *gl; char *path; Dir *d; if(arg) { argc = getfields(arg, argv, sizeof(argv)-1, 1, " \t"); } else { argc = 1; argv[0] = ftpd->user.cwd; } data = dialdata(ftpd, 0); if(!data) return reply(ftpd->out, "500 List failed: couldn't dial data"); for(i=0;i<argc;i++) { gl = glob(argv[i]); if(!gl) continue; while(path = globiter(gl)) { cleanname(path); logit("list: path %s user %s", path, ftpd->user.name); d = dirstat(path); if(d->mode & DMDIR) listdir(ftpd, data, path, fn); else (*fn)(data, d, nil); free(d); } } closedata(ftpd, data, 0); return 0; } char * mode2asc(int m) { char *asc; char *p; asc = strdup("----------"); if(DMDIR & m) asc[0] = 'd'; if(DMAPPEND & m) asc[0] = 'a'; else if(DMEXCL & m) asc[3] = 'l'; for(p = asc + 1; p < asc + 10; p += 3, m <<= 3) { if(m & 0400) p[0] = 'r'; if(m & 0200) p[1] = 'w'; if(m & 0100) p[2] = 'x'; } return asc; } void listprint(Biobuf *data, Dir *d, char *dirname) { char *ts, *mode; ts = strdup(ctime(d->mtime)); ts[16] = '\0'; if(time(0) - d->mtime > 6 * 30 * 24 * 60 * 60) memmove(ts + 11, ts + 23, 5); mode = mode2asc(d->mode); if(dirname) reply(data, "%s %3d %-8s %-8s %7lld %s %s/%s", mode, 1, d->uid, d->gid, d->length, ts + 4, dirname, d->name); else reply(data, "%s %3d %-8s %-8s %7lld %s %s", mode, 1, d->uid, d->gid, d->length, ts + 4, d->name); free(mode); free(ts); } int listcmd(Ftpd *ftpd, char *arg) { return list(ftpd, arg, listprint); } int loginuser(Ftpd *ftpd, char *pass, char *nsfile) { char *user; user = ftpd->user.name; putenv("service", "ftp"); if(!ftpd->user.isnone) { if(login(user, pass, nsfile) < 0) return reply(ftpd->out, "530 Not logged in: bad password"); } else { if(newns(user, nsfile) < 0) return reply(ftpd->out, "530 Not logged in: user out of service"); }