/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include "uucp.h" #include "log.h" void notify(), lnotify(), unlinkdf(), arrived(); static void stmesg(); static int nospace(); struct Proto { char P_id; int (*P_turnon)(); int (*P_rdmsg)(); int (*P_wrmsg)(); int (*P_rddata)(); int (*P_wrdata)(); int (*P_turnoff)(); }; extern char _Protocol[]; extern char *findProto(); extern char uuxqtarg[]; extern int gturnon(), gturnoff(); extern int grdmsg(), grddata(); extern int gwrmsg(), gwrdata(); extern int wmesg(), rmesg(), expfile(), putinpub(), stptcl(); extern void setline(), TMname(), cleanup(), pfEndfile(), statlog(), mailst(); #ifdef D_PROTOCOL extern int dturnon(), dturnoff(); extern int drdmsg(), drddata(); extern int dwrmsg(), dwrdata(); #endif /* D_PROTOCOL */ #ifdef X_PROTOCOL extern int xturnon(), xturnoff(); extern int xrdmsg(), xrddata(); extern int xwrmsg(), xwrdata(); #endif /* X_PROTOCOL */ #ifdef E_PROTOCOL extern int eturnon(), eturnoff(); extern int erdmsg(), erddata(); extern int ewrmsg(), ewrdata(); extern int trdmsg(), twrmsg(); extern int trddata(), twrdata(); #endif /* E_PROTOCOL */ #ifdef F_PROTOCOL extern int fturnon(), fturnoff(); extern int frdmsg(), frddata(); extern int fwrmsg(), fwrdata(); #endif /* F_PROTOCOL */ extern int imsg(); extern int omsg(); extern int turnoff(); extern long strtol(); struct Proto Ptbl[]={ {'g', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff}, {'G', gturnon, grdmsg, gwrmsg, grddata, gwrdata, gturnoff}, #ifdef E_PROTOCOL {'e', eturnon, erdmsg, ewrmsg, erddata, ewrdata, eturnoff}, {'t', eturnon, trdmsg, twrmsg, trddata, twrdata, eturnoff}, #endif /* E_PROTOCOL */ #ifdef D_PROTOCOL {'d', dturnon, drdmsg, dwrmsg, drddata, dwrdata, dturnoff}, #endif /* D_PROTOCOL */ #ifdef X_PROTOCOL {'x', xturnon, xrdmsg, xwrmsg, xrddata, xwrdata, xturnoff}, #endif /* X_PROTOCOL */ #ifdef F_PROTOCOL {'f', fturnon, frdmsg, fwrmsg, frddata, fwrdata, fturnoff}, #endif /* F_PROTOCOL */ '\0' }; #define VALIDSIZE sizeof(Ptbl)/sizeof(struct Proto) int (*Rdmsg)()=imsg, (*Rddata)(); int (*Wrmsg)()=omsg, (*Wrdata)(); int (*Turnon)(), (*Turnoff)()=turnoff; #define YES "Y" #define NO "N" #define TBUFSIZE 128 /* temporary buffer size */ #define FLENRADIX (16) /* output radix for file start point */ /* * failure messages */ #define EM_MAX 10 #define EM_LOCACC "N1" /* local access to file denied */ #define EM_RMTACC "N2" /* remote access to file/path denied */ #define EM_BADUUCP "N3" /* a bad uucp command was generated */ #define EM_NOTMP "N4" /* remote error - can't create temp */ #define EM_RMTCP "N5" /* can't copy to remote directory - file in public */ #define EM_LOCCP "N6" /* can't copy on local system */ #define EM_SEEK "N7" /* can't seek to checkpoint */ /* EM_ "N8" */ /* placeholder*/ /* EM_ "N9" */ /* placeholder*/ #define EM_ULIMIT "N10" /* receiver ulimit exceeded */ char *Em_msg[] = { "COPY FAILED (reason not given by remote)", "local access to file denied", "remote access to path/file denied", "system error - bad uucp command generated", "remote system can't create temp file", "can't copy to file/directory - file left in PUBDIR/user/file", "can't copy to file/directory - file left in PUBDIR/user/file", "can't seek to checkpoint", "COPY FAILED (reason not given by remote)", /* placeholder */ "COPY FAILED (reason not given by remote)", /* placeholder */ "file exceeds ulimit of receiving system", "forwarding error" }; #define XUUCP 'X' /* execute uucp (string) */ #define SLTPTCL 'P' /* select protocol (string) */ #define USEPTCL 'U' /* use protocol (character) */ #define RCVFILE 'R' /* receive file (string) */ #define SNDFILE 'S' /* send file (string) */ #define RQSTCMPT 'C' /* request complete (string - yes | no) */ #define HUP 'H' /* ready to hangup (string - yes | no) */ #define RESET 'X' /* reset line modes */ #define W_MAX 10 /* maximum number of C. files per line */ #define W_MIN 7 /* min number of entries */ #define W_TYPE wrkvec[0] #define W_FILE1 wrkvec[1] #define W_FILE2 wrkvec[2] #define W_USER wrkvec[3] #define W_OPTNS wrkvec[4] #define W_DFILE wrkvec[5] #define W_MODE wrkvec[6] #define W_NUSER wrkvec[7] #define W_SFILE wrkvec[8] #define W_RDFILE wrkvec[8] #define W_POINT wrkvec[9] #define W_FSIZE wrkvec[9] #define W_RFILE wrkvec[5] #define W_XFILE wrkvec[5] char *mf; #define RMESG(m, s) if (rmesg(m, s) != 0) {(*Turnoff)(); return(FAIL);} #define RAMESG(s) if (rmesg('\0', s) != 0) {(*Turnoff)(); return(FAIL);} #define WMESG(m, s) if(wmesg(m, s) != 0) {(*Turnoff)(); return(FAIL);} char Wfile[MAXFULLNAME] = {'\0'}; char Dfile[MAXFULLNAME]; char *wrkvec[W_MAX+1]; int statfopt; /* * Create restart point filename */ static void Pname(fileid, dfile, direct) char *fileid; char *dfile; int direct; /* indicates a direct delivery temp file nameneeded */ { char *p; /* * If the file is direct delivery, then its name is: * * /dir/dir/dir/.Pnnnnnnnn * * in the target directory. We create this by replacing the * name of the target file with the D.nnnnnn name from the * work vector, and then overwriting the D. with .P */ if (direct) { if (p = strrchr(dfile, '/')) { /* find the last slash */ p++; strcpy(p, fileid); /* append D.nnnnn name to dir */ *p++ = '.'; *p = 'P'; /* replace beginning with .P */ DEBUG(7, "Point file (direct) =%s\n", dfile); return; } } strcpy(dfile, RemSpool); strcat(dfile, "/"); p = dfile + strlen(Dfile); strcat(dfile, fileid); *p = 'P'; DEBUG(7, "Point file=%s\n", dfile); return; } /* * execute the conversation between the two machines * after both programs are running. * returns: * SUCCESS -> ok * FAIL -> failed */ int cntrl() { FILE * fp; struct stat stbuf; char * p; long startp; /* checkpoint restart point */ long actualsize; /* actual file size */ long im; long lfilemode; mode_t filemode; int status; int i, narg; int mailopt, ntfyopt; int ret; char tbuf[TBUFSIZE]; char rqstr[BUFSIZ]; /* contains the current request message */ char msg[BUFSIZ]; char filename[MAXFULLNAME], wrktype; char fsize[NAMESIZE]; /* holds file size/checkpoint string */ char localname[MAXFULLNAME]; /* real local system name */ char Recspool[MAXFULLNAME]; /* spool area for slave uucico */ static pid_t pnum; extern int uuxqtflag; /* set if received X. or D. file */ pnum = getpid(); Wfile[0] = '\0'; (void) sprintf(Recspool, "%s/%s", SPOOL, Rmtname); top: (void) strcpy(User, Uucp); statfopt = 0; *Jobid = '\0'; DEBUG(4, "*** TOP *** - Role=%d, ", Role); setline(RESET); if (Role == MASTER) { /* * get work */ pfFindFile(); if ((narg = gtwvec(Wfile, wrkvec, W_MAX)) == 0) { acEnd(COMPLETE); /*stop collecting accounting log */ WMESG(HUP, ""); /* I(master) am done. want me to quit? */ RMESG(HUP, msg); goto process; } DEBUG(7, "Wfile - %s,", Wfile); strncpy(Jobid, BASENAME(Wfile, '/')+2, NAMESIZE); Jobid[NAMESIZE-1] = '\0'; DEBUG(7, "Jobid = %s\n", Jobid); wrktype = W_TYPE[0]; pfFound(Jobid, W_OPTNS, Nstat.t_qtime); mailopt = strchr(W_OPTNS, 'm') != NULL; statfopt = strchr(W_OPTNS, 'o') != NULL; ntfyopt = strchr(W_OPTNS, 'n') != NULL; uucpname(localname); /* get real local machine name */ acDojob(Jobid, localname, W_USER); scRequser(W_USER); /* log requestor user id */ /* * We used to check for corrupt workfiles here (narg < 5), * but we were doing it wrong, and besides, anlwrk.c is the * appropriate place to do it. */ (void) sprintf(User, "%s", W_USER); if (wrktype == SNDFILE ) { (void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Myname, W_FILE1, Rmtname, W_FILE2, User); /* log destination node, user and file name */ scDest(Rmtname,NOTAVAIL,W_FILE2); /* log source node, file owner, file name, mod time and size */ scSrc(Myname,scOwn(W_FILE1),W_FILE1,scMtime(W_FILE1) ,scSize(W_FILE1)); logent(rqstr, "REQUEST"); CDEBUG(1, "Request: %s\n", rqstr); mf = W_SFILE; (void) strcpy(filename, W_FILE1); expfile(filename); (void) strcpy(Dfile, W_DFILE); if ( (fp = fopen(Dfile, "r")) == NULL) { if ( (fp = fopen(filename, "r")) == NULL) { /* cannot read spool or original file */ unlinkdf(Dfile); lnotify(User, rqstr, "can't access"); (void) sprintf(msg, "CAN'T READ %s %d", filename, errno); logent(msg, "FAILED"); CDEBUG(1, "Failed: Can't Read %s\n", filename); scWrite(); /* log the security violation */ goto top; } else { /* ensure original file is publicly readable */ if ( !F_READANY(fileno(fp)) ) { /* access denied */ logent("DENIED", "ACCESS"); unlinkdf(W_DFILE); lnotify(User, rqstr, "access denied"); CDEBUG(1, "Failed: Access Denied\n%s", ""); scWrite(); /* log the security violation */ goto top; } } } if (Restart && !(fstat(fileno(fp), &stbuf))) { (void) sprintf(fsize, "0x%lx", stbuf.st_size); W_FSIZE = fsize; /* set file size in vector */ } /* Check whether remote's ulimit is exceeded */ if (SizeCheck) { if (((stbuf.st_size-1)/512 + 1) > RemUlimit) { /* remote ulimit exceeded */ unlinkdf(Dfile); lnotify(User, rqstr, "remote ulimit exceeded"); logent("DENIED", "REMOTE ULIMIT EXCEEDED"); CDEBUG(1, "Denied: remote ulimit exceeded %s\n", filename); scWrite(); (void) fclose(fp); goto top; } } } if (wrktype == RCVFILE) { (void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Rmtname, W_FILE1, Myname, W_FILE2, User); /* log destination node, user and file name */ scDest(Myname,NOTAVAIL,W_FILE2); /* log source node, file owner, file name, mod time and size */ scSrc(Rmtname,NOTAVAIL,W_FILE1,NOTAVAIL,NOTAVAIL); logent(rqstr, "REQUEST"); CDEBUG(1, "Request: %s\n", rqstr); mf = W_RFILE; (void) strcpy(filename, W_FILE2); /* change Wrkdir to SPOOL/Rmtname in case the file being ** requested is needed for some remote execution. */ (void) strcpy(Wrkdir, Recspool); expfile(filename); /* now change Wrkdir back to what it was ** just being paranoid. */ (void) strcpy(Wrkdir, RemSpool); if (chkperm(W_FILE1, filename, strchr(W_OPTNS, 'd'))) { /* access denied */ logent("DENIED", "ACCESS"); lnotify(User, rqstr, "access denied"); CDEBUG(1, "Failed: Access Denied--File: %s\n", filename); scWrite(); /* log the security violation */ goto top; } /* * If we are not going to spool the file in the spool * directory, just use the destination file name. If we * are not supporting restart, wipe out the target file. * else: * * If restart is enabled, make up the Point file name * as the file to open, else use the TM style name. * * If we run into a spool name of "D.0", this implies * that someone forgot to install the new uucp and * uux commands. Such jobs will not be checkpointed. */ if (Restart && (strlen(W_RDFILE) > (size_t) 6)) { if (noSpool()) { strcpy(Dfile, filename); /* use Dest file directly */ Pname(W_RDFILE, Dfile, TRUE); } else Pname(W_RDFILE, Dfile, FALSE); } else { TMname(Dfile, pnum); /* get TM file name */ unlink(Dfile); } /* * If the spool file exists, it better have the right owner * and permissions! */ if (Restart && noSpool()) { if ((! stat(Dfile, &stbuf)) && ((stbuf.st_mode != (DFILEMODE|S_IFREG)) || ((stbuf.st_gid != UUCPGID) || (stbuf.st_uid != UUCPUID)))) { lnotify(User, rqstr, "bad spool file ownership/permissions"); logent("BAD DESTFILE OWNER/PERMS", "FAIL"); CDEBUG(1, "Failed: bad dest file owner/perms 0%o; fail\n", stbuf.st_mode); goto top; } } if ( ((fp = fopen(Dfile, "a+")) == NULL) || nospace(Dfile)) { /* can not create temp */ if (noSpool()) logent("CAN'T CREATE/OPEN DEST FILE", "FAILED"); else logent("CAN'T CREATE TM FILE", "FAILED"); CDEBUG(1, "Failed: No Space!\n%s", ""); unlinkdf(Dfile); assert(Ct_CREATE, Dfile, nospace(Dfile), __FILE__, __LINE__); cleanup(FAIL); } /* * Send the W_POINT value to the other side. */ if (Restart) { if (fstat (fileno(fp), &stbuf)) { logent("CAN'T STAT DFILE", "START FROM BEGINNING"); stbuf.st_size = 0L; } /* * find a good start point. Take care of simple * underflow and the signed nature of longs. */ DEBUG(7, "Dfile length 0x%lx\n", stbuf.st_size); startp = stbuf.st_size - (stbuf.st_size % BUFSIZ); if((stbuf.st_size >= 0) && (startp < 0)) startp = 0; if(startp) { if(startp < 0) sprintf(tbuf,"start=0x%lx", startp); else sprintf(tbuf,"start=%ld", startp); logent(tbuf, "RESTART"); } sprintf(fsize, "0x%lx", startp); W_POINT = fsize; /* set start point in vector */ if (lseek(fileno(fp), startp, 0) == -1) { WMESG(SNDFILE, EM_SEEK); logent("CAN'T SEEK", "DENIED"); CDEBUG(1, "Failed, Can't seek in Dfile\n%s", ""); unlinkdf(Dfile); goto top; } fp->_cnt = 0; fp->_ptr = fp->_base; } Seqn++; chmod(Dfile, DFILEMODE); /* no peeking! */ chown(Dfile, UUCPUID, UUCPGID); } DEBUG(4, "wrktype - %c\n ", wrktype); /* Build up the message itself */ msg[0] = '\0'; for (i = 1; i < narg; i++) { (void) strcat(msg, " "); (void) strcat(msg, wrkvec[i]); } WMESG(wrktype, msg); /* I(master) am sending you our work file */ RMESG(wrktype, msg); /* I(master) am waiting for your response */ goto process; } /* * role is slave */ RAMESG(msg); /* I(slave) am waiting for our work file */ process: DEBUG(4, " PROCESS: msg - %s\n", msg); switch (msg[0]) { case RQSTCMPT: DEBUG(4, "%s\n", "RQSTCMPT:"); if (msg[1] == 'N') { i = atoi(&msg[2]); if (i < 0 || i > EM_MAX) i = 0; logent(Em_msg[i], "REQUESTED"); } if (Role == MASTER) { notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]); } pfEndfile(""); /* "" indicates the file transfer completely */ goto top; case HUP: DEBUG(4, "%s\n", "HUP:"); if (msg[1] == 'Y') { WMESG(HUP, YES); /* let's quit */ (*Turnoff)(); Rdmsg = imsg; Wrmsg = omsg; Turnoff = turnoff; return(0); } if (msg[1] == 'N') { ASSERT(Role == MASTER, Wr_ROLE, "", Role); Role = SLAVE; scReqsys(Rmtname); /* log requestor system */ chremdir(Rmtname); goto top; } /* * get work */ if ( (switchRole() == FALSE) || !iswrk(Wfile) ) { DEBUG(5, "SLAVE-switchRole (%s)\n", switchRole() ? "TRUE" : "FALSE"); WMESG(HUP, YES); /* let's quit */ RMESG(HUP, msg); goto process; } /* Note that Wfile is the first C. to process at top * set above by iswrk() call */ if (uuxqtflag) { xuuxqt(uuxqtarg); uuxqtflag = 0; } WMESG(HUP, NO); /* don't quit. I(slave) have more to do */ Role = MASTER; uucpname(localname); /* get real local machine name */ scReqsys(localname); /* log requestor system */ acInit("xfer"); goto top; case XUUCP: /* * slave part * No longer accepted */ WMESG(XUUCP, NO); goto top; case SNDFILE: /* * MASTER section of SNDFILE */ DEBUG(4, "%s\n", "SNDFILE:"); if (msg[1] == 'N') { i = atoi(&msg[2]); if (i < 0 || i > EM_MAX) i = 0; logent(Em_msg[i], "REQUEST"); notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]); ASSERT(Role == MASTER, Wr_ROLE, "", Role); (void) fclose(fp); /* if remote is out of tmp space, then just hang up */ ASSERT(i != 4, Em_msg[4], Rmtname, i); /* EM_NOTMP */ unlinkdf(W_DFILE); scWrite(); /* something is wrong on other side, log the security violation */ Seqn++; goto top; } if (msg[1] == 'Y') { /* * send file */ ASSERT(Role == MASTER, Wr_ROLE, "", Role); if (fstat(fileno(fp), &stbuf)) /* never fail but .. */ stbuf.st_size = 0; /* for time loop calculation */ /* * If checkpoint restart is enabled, seek to the * starting point in the file. We use hex because * C doesn't support unsigned long directly. */ if (Restart) { if((startp = strtol(&msg[2], (char **) 0, FLENRADIX))) { CDEBUG(1, "Restart point=0x%lx\n", startp); if(startp < 0) sprintf(tbuf,"start=0x%lx", startp); else sprintf(tbuf,"start=%ld", startp); p = tbuf + strlen(tbuf); if (stbuf.st_size < 0) sprintf(p,", length=0x%lx", stbuf.st_size); else sprintf(p,", length=%ld", stbuf.st_size); logent(tbuf, "RESTART"); errno = 0; if (lseek(fileno(fp), startp, 0) == -1) { logent(strerror(errno), "FSEEK ERROR"); (void) fclose(fp); (*Turnoff)(); Seqn++; return(FAIL); } fp->_cnt = 0; fp->_ptr = fp->_base; } } (void) millitick(); /* start msec timer */ pfStrtXfer(MCHAR, SNDFILE); scStime(); /* log start transfer time for security log */ /* (ret != 0) implies the trammission error occurred. If checkpoint protocol is available then the next transfer will restart from the breakpoint of the file, otherwise from the beginning of the file */ ret = (*Wrdata)(fp, Ofn); /* the second millitick() returns the duration between the first and second call. writes "PARTIAL FILE to the transfer log indicating a transmission error. */ statlog( "->", getfilesize(), millitick(), (ret) ? "PARTIAL FILE" : "" ); acInc(); /* increment job size in accounting log */ pfEndXfer(); scEtime(); /* log end transfer time for security log */ Seqn++; (void) fclose(fp); if (ret != 0) { pfEndfile("PARTIAL FILE"); acEnd(PARTIAL); /*stop collecting accounting log */ (*Turnoff)(); return(FAIL); } /* loop depending on the size of the file */ /* give an extra try for each megabyte */ for (im = stbuf.st_size >> 10; im >= 0; --im) { if ((ret = rmesg(RQSTCMPT, msg)) == 0) break; /* got message */ } if (ret != 0) { (*Turnoff)(); return(FAIL); } unlinkdf(W_DFILE); goto process; } /* * SLAVE section of SNDFILE */ ASSERT(Role == SLAVE, Wr_ROLE, "", Role); /* * request to receive file * check permissions */ i = getargs(msg, wrkvec, W_MAX); scRequser(W_USER); /* log requestor user id */ /* log destination node, user and file name */ scDest(Myname,NOTAVAIL,W_FILE2); /* log source node, file owner, file name, mod time and size */ scSrc(Rmtname,NOTAVAIL,W_FILE1,NOTAVAIL,NOTAVAIL); /* Check for bad request */ if (i < W_MIN) { WMESG(SNDFILE, EM_BADUUCP); /* you(remote master) gave me bad work file */ logent("DENIED", "TOO FEW ARGS IN SLAVE SNDFILE"); goto top; } /* SLAVE gets the original filesize from sender (MASTER) */ /* This will be used to check the length of the P. file */ if (Restart) { if (W_FSIZE && (*W_FSIZE != '\0')) { actualsize = strtol(W_FSIZE, (char **) 0, FLENRADIX); CDEBUG(7, "Actual File Length %ld\n", actualsize); } else { actualsize = -1; CDEBUG(7, "Actual File Length Not Provided\n%s", ""); } } mf = W_SFILE; (void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Rmtname, W_FILE1, Myname, W_FILE2, W_USER); logent(rqstr, "REMOTE REQUESTED"); DEBUG(4, "msg - %s\n", msg); CDEBUG(1, "Remote Requested: %s\n", rqstr); (void) strcpy(filename, W_FILE2); expfile(filename); DEBUG(4, "SLAVE - filename: %s\n", filename); if (chkpth(filename, CK_WRITE) || chkperm(W_FILE1, filename, strchr(W_OPTNS, 'd'))) { WMESG(SNDFILE, EM_RMTACC); /* you(remote master) can't send data to this file(directory) */ logent("DENIED", "PERMISSION"); CDEBUG(1, "Failed: Access Denied\n%s", ""); scWrite(); /* log security violation */ goto top; } (void) sprintf(User, "%s", W_USER); DEBUG(4, "chkpth ok Rmtname - %s\n", Rmtname); if (Restart && (strlen(W_DFILE) > (size_t) 6)) { if (noSpool()) { strcpy(Dfile, filename); /* use Dest file directly */ Pname(W_DFILE, Dfile, TRUE); if (! Restart) unlink(Dfile); } else Pname(W_DFILE, Dfile, FALSE); } else { TMname(Dfile, pnum); /* get TM file name */ unlink(Dfile); } /* * If the spool file exists, it better have the right owner * and permissions! */ if (Restart && noSpool()) { if ((! stat(Dfile, &stbuf)) && ((stbuf.st_mode != (DFILEMODE|S_IFREG)) || ((stbuf.st_gid != UUCPGID) || (stbuf.st_uid != UUCPUID)))) { WMESG(SNDFILE, EM_NOTMP); /* I(slave) see bad perms */ logent("BAD DESTFILE OWNER/PERMS", "FAILED"); CDEBUG(1, "Failed: bad dest file owner/perms 0%o\n", stbuf.st_mode); goto top; } } if ( ((fp = fopen(Dfile, "a+")) == NULL) || nospace(Dfile) ) { WMESG(SNDFILE, EM_NOTMP); /* I(slave) can't create TM file */ logent("CAN'T OPEN", "DENIED"); CDEBUG(1, "Failed: Can't Create Temp File\n%s", ""); unlinkdf(Dfile); goto top; } chmod(Dfile, DFILEMODE); /* no peeking! */ chown(Dfile, UUCPUID, UUCPGID); if (Restart && (strlen(W_DFILE) > (size_t) 6)) { if(fstat(fileno(fp), &stbuf)) { WMESG(SNDFILE, EM_NOTMP); logent("CAN'T STAT", "DENIED"); CDEBUG(1, "Failed: Can't Stat Temp File\n%s", ""); unlinkdf(Dfile); Seqn++; goto top; } /* * find a good start point. Take care of simple underflow * and the signed nature of longs. */ DEBUG(7, "Dfile length 0x%lx\n", stbuf.st_size); startp = stbuf.st_size - (stbuf.st_size % BUFSIZ); if((stbuf.st_size >= 0) && (startp < 0)) startp = 0; if(startp) { if(startp < 0) sprintf(tbuf,"start=0x%lx", startp); else sprintf(tbuf,"start=%ld", startp); logent(tbuf, "RESTART"); } sprintf(tbuf, "%s 0x%lx", YES, startp); if (lseek(fileno(fp), startp, 0) == -1) { WMESG(SNDFILE, EM_SEEK); logent("CAN'T SEEK", "DENIED"); CDEBUG(1, "Failed, Can't seek in Dfile\n%s", ""); unlinkdf(Dfile); Seqn++; goto top; } fp->_cnt = 0; fp->_ptr = fp->_base; CDEBUG(1," restart msg %s\n", tbuf); WMESG(SNDFILE, tbuf); } else WMESG(SNDFILE, YES); /* I(slave) clear to send */ (void) millitick(); /* start msec timer */ pfStrtXfer(SCHAR, RCVFILE); scStime(); /* log start transfer time for security log */ /* (ret != 0) implies the trammission error occurred. If checkpoint protocol is available then the next recieve will restart from the breakpoint of the file, otherwise from the beginning of the file */ setline(RCVFILE); ret = (*Rddata)(Ifn, fp); setline(SNDFILE); /* the second millitick() returns the duration between the first and second call. writes "PARTIAL FILE to the transfer log indicating a transmission error. */ statlog( "<-", getfilesize(), millitick(), (ret) ? "PARTIAL FILE" : "" ); pfEndXfer(); scEtime(); /* log end transfer time for security log */ Seqn++; if (ret != 0) { pfEndfile("PARTIAL FILE"); (void) fclose(fp); if ( ret == EFBIG ) { WMESG(RQSTCMPT, EM_ULIMIT); logent("FILE EXCEEDS ULIMIT","FAILED"); CDEBUG(1, "Failed: file size exceeds ulimit%s\n", ""); goto top; } (*Turnoff)(); logent("INPUT FAILURE", "IN SEND/SLAVE MODE"); return(FAIL); } if (Restart && (actualsize != -1)) { if (fstat(fileno(fp), &stbuf)) { (void) fclose(fp); unlinkdf(Dfile); (*Turnoff)(); logent("CAN'T STAT PFILE", "FAILED"); return(FAIL); } if (stbuf.st_size != actualsize) { (void) fclose(fp); unlinkdf(Dfile); (*Turnoff)(); logent("RECEIVED SIZE NOT EQUAL TO ACTUAL SIZE", "FAILED"); CDEBUG(1, "Failed: receive size %ld ", stbuf.st_size); CDEBUG(1, "not equal to actual size %ld\n", actualsize); return(FAIL); } } (void) fclose(fp); /* copy to user directory */ ntfyopt = strchr(W_OPTNS, 'n') != NULL; /* * See if spool file and target file in the same file system */ ret = 0; if (p = strrchr(Dfile, '/')) { *p = '\0'; ret = PREFIX(Dfile, filename); *p = '/'; } if (noSpool() && ret) { /* * if we are not already in the right file, and * it is theoretically in the same file system, * link it there... */ if(strcmp (filename, Dfile)) { unlink(filename); if(link(Dfile, filename)) { logent("FAILED", "MOVE"); scWrite(); putinpub(filename, Dfile, BASENAME(W_USER,'!')); } else DEBUG(7, "linked Point file to %s\n", filename); unlink(Dfile); } else DEBUG(7, "Point file and %s the same\n", filename); status = 0; /* all done */ } else status = xmv(Dfile, filename); scSize(Dfile); /* log source file size */ WMESG(RQSTCMPT, status ? EM_RMTCP : YES); if (status == 0) { sscanf(W_MODE, "%lo", &lfilemode); if (lfilemode <= 0) filemode = PUB_FILEMODE; else filemode = (mode_t)lfilemode; if (PREFIX(RemSpool, filename)) chmod(filename, DFILEMODE); else chmod(filename, (filemode & LEGALMODE) | PUB_FILEMODE); arrived(ntfyopt, filename, W_NUSER, Rmtname, User); } else { logent("FAILED", "COPY"); scWrite(); /* log the security violation */ status = putinpub(filename, Dfile, BASENAME(W_USER, '!')); DEBUG(4, "->PUBDIR %d\n", status); if (status == 0) arrived(ntfyopt, filename, W_NUSER, Rmtname, User); } pfEndfile(""); /* "" indicates the file transfer completely */ if ( W_FILE2[1] == '.' && (W_FILE2[0] == XQTPRE || W_FILE2[0] == DATAPRE) ) uuxqtflag = 1; goto top; case RCVFILE: /* * MASTER section of RCVFULE */ DEBUG(4, "%s\n", "RCVFILE:"); if (msg[1] == 'N') { i = atoi(&msg[2]); if (i < 0 || i > EM_MAX) i = 0; logent(Em_msg[i], "REQUEST"); notify(mailopt, W_USER, rqstr, Rmtname, &msg[1]); ASSERT(Role == MASTER, Wr_ROLE, "", Role); (void) fclose(fp); unlinkdf(Dfile); scWrite(); /* something is wrong on other side, log the security violation */ goto top; } if (msg[1] == 'Y') { /* MASTER gets the original filesize from sender (SLAVE) */ /* This will be used to check the length of the P. file */ if (Restart) { *fsize = '\0'; sscanf(&msg[2], "%*o %s", fsize); if (*fsize != '\0') { actualsize = strtol(fsize, (char **) 0, FLENRADIX); CDEBUG(7, "Actual File Length %ld\n", actualsize); } else { actualsize = -1; CDEBUG(7, "Actual File Length Not Provided\n%s", ""); } } /* * receive file */ ASSERT(Role == MASTER, Wr_ROLE, "", Role); (void) millitick(); /* start msec timer */ pfStrtXfer(MCHAR, SNDFILE); scStime(); /* (ret != 0) implies the trammission error occurred. If checkpoint protocol is available then the next recieve will restart from the breakpoint of the file, otherwise from the beginning of the file */ ret = (*Rddata)(Ifn, fp); /* the second millitick() returns the duration between the first and second call. writes "PARTIAL FILE to the transfer log indicating a transmission error. */ statlog( "<-", getfilesize(), millitick(), (ret) ? "PARTIAL FILE" : "" ); pfEndXfer(); scEtime(); if (ret != 0) { pfEndfile("PARTIAL FILE"); (void) fclose(fp); if ( ret == EFBIG ) { WMESG(RQSTCMPT, EM_ULIMIT); logent("FILE EXCEEDS ULIMIT","FAILED"); CDEBUG(1, "Failed: file size exceeds ulimit%s\n", ""); goto top; } (*Turnoff)(); logent("INPUT FAILURE", "IN RECEIVE/MASTER MODE"); return(FAIL); } if (Restart && (actualsize != -1)) { if (fstat(fileno(fp), &stbuf)) { (void) fclose(fp); unlinkdf(Dfile); (*Turnoff)(); logent("CAN'T STAT PFILE", "FAILED"); return(FAIL); } if (stbuf.st_size != actualsize) { (void) fclose(fp); unlinkdf(Dfile); (*Turnoff)(); logent("RECEIVED SIZE NOT EQUAL TO ACTUAL SIZE", "FAILED"); CDEBUG(1, "Failed: receive size %ld ", stbuf.st_size); CDEBUG(1, "not equal to actual size %ld\n", actualsize); return(FAIL); } } (void) fclose(fp); /* * See if spool file and target file in the same file system */ ret = 0; if (p = strrchr(Dfile, '/')) { *p = '\0'; ret = PREFIX(Dfile, filename); *p = '/'; } if (noSpool() && ret) { /* * if we are not already in the right file, and * it is theoretically in the same file system, * link it there... */ if(strcmp (filename, Dfile)) { unlink(filename); if(link(Dfile, filename)) { logent("FAILED", "MOVE"); scWrite(); putinpub(filename, Dfile, W_USER); } else DEBUG(7, "linked Point file to %s\n", filename); unlink(Dfile); } else DEBUG(7, "Point file and %s the same\n", filename); status = 0; /* all done */ } else status = xmv(Dfile, filename); WMESG(RQSTCMPT, status ? EM_RMTCP : YES); notify(mailopt, W_USER, rqstr, Rmtname, status ? EM_LOCCP : YES); if (status == 0) { sscanf(&msg[2], "%lo", &lfilemode); if (lfilemode <= 0) filemode = PUB_FILEMODE; else filemode = (mode_t)lfilemode; if (PREFIX(RemSpool, filename)) chmod(filename, DFILEMODE); else chmod(filename, (filemode & LEGALMODE) | PUB_FILEMODE); } else { logent("FAILED", "COPY"); scWrite(); /* log the security violation */ putinpub(filename, Dfile, W_USER); } pfEndfile(""); /* "" indicates the file transfer completely */ if ( W_FILE2[1] == '.' && (W_FILE2[0] == XQTPRE || W_FILE2[0] == DATAPRE) ) uuxqtflag = 1; goto top; } /* * SLAVE section of RCVFILE * (request to send file) */ ASSERT(Role == SLAVE, Wr_ROLE, "", Role); /* check permissions */ i = getargs(msg, wrkvec, W_MAX); scRequser(W_USER); /* log requestor user id */ /* log destination node, user and file name */ scDest(Rmtname,NOTAVAIL,W_FILE2); /* log source node, file owner, file name, mod time and size */ scSrc(Myname,scOwn(W_FILE1),W_FILE1,scMtime(W_FILE1),scSize(W_FILE1)); /* Check for bad request */ if (i < 5) { WMESG(RCVFILE, EM_BADUUCP); /* you(remote master) gave me bad work file */ logent("DENIED", "TOO FEW ARGS IN SLAVE RCVFILE"); goto top; } (void) sprintf(rqstr, "%s!%s --> %s!%s (%s)", Myname, W_FILE1, Rmtname, W_FILE2, W_USER); logent(rqstr, "REMOTE REQUESTED"); CDEBUG(1, "Remote Requested: %s\n", rqstr); mf = W_RFILE; DEBUG(4, "msg - %s\n", msg); DEBUG(4, "W_FILE1 - %s\n", W_FILE1); (void) strcpy(filename, W_FILE1); expfile(filename); if (DIRECTORY(filename)) { (void) strcat(filename, "/"); (void) strcat(filename, BASENAME(W_FILE2, '/')); } (void) sprintf(User, "%s", W_USER); if (requestOK() == FALSE) { /* remote can't request data from my system */ WMESG(RCVFILE, EM_RMTACC); logent("DENIED", "REQUESTING"); CDEBUG(1, "Failed: Access Denied\n%s", ""); scWrite(); /* log the security violation */ goto top; } DEBUG(4, "requestOK for Loginuser - %s\n", Loginuser); if ((fp = fopen(filename, "r")) == NULL) { WMESG(RCVFILE, EM_RMTACC); /* you(remote master) can't read my file */ logent("CAN'T OPEN", "DENIED"); CDEBUG(1, "Failed: Can't Open %s\n", filename); scWrite(); /* log the security violation */ goto top; } if (chkpth(filename, CK_READ) || !F_READANY(fileno(fp))) { WMESG(RCVFILE, EM_RMTACC); /* you(remote master) can't retrive my file */ logent("DENIED", "PERMISSION"); CDEBUG(1, "Failed: Access Denied\n%s", ""); scWrite(); /* log the security violation */ fclose(fp); goto top; } DEBUG(4, "chkpth ok Loginuser - %s\n", Loginuser); ASSERT(fstat(fileno(fp), &stbuf) == 0, Ct_STAT, filename, errno); /* Check whether remote's ulimit is exceeded */ if (SizeCheck) { if (((stbuf.st_size-1)/512 + 1) > RemUlimit) { /* remote ulimit exceeded */ WMESG(RCVFILE, EM_ULIMIT); logent("DENIED", "REMOTE ULIMIT EXCEEDED"); CDEBUG(1, "Denied: remote ulimit exceeded %s\n", filename); scWrite(); (void) fclose(fp); goto top; } } /* * ok to send file */ if (Restart && i >= 10) { if (startp = strtol(W_POINT, (char **) 0, FLENRADIX)) { CDEBUG(1,"Restart point=0x%lx\n", startp); errno = 0; if (lseek(fileno(fp), startp, 0) == -1) { WMESG(RCVFILE, EM_SEEK); logent(strerror(errno), "FSEEK ERROR"); (void) fclose(fp); goto top; } fp->_cnt = 0; fp->_ptr = fp->_base; if(startp < 0) sprintf(tbuf,"start=0x%lx", startp); else sprintf(tbuf,"start=%ld", startp); p = tbuf + strlen(tbuf); if (stbuf.st_size < 0) sprintf(p,", length=0x%lx", stbuf.st_size); else sprintf(p,", length=%ld", stbuf.st_size); logent(tbuf, "RESTART"); } } if (Restart) (void) sprintf(msg, "%s %lo 0x%lx", YES, (long) (stbuf.st_mode & LEGALMODE), (long) stbuf.st_size); else (void) sprintf(msg, "%s %lo", YES, (long) (stbuf.st_mode & LEGALMODE)); WMESG(RCVFILE, msg); /* I(slave) send you my file now */ Seqn++; (void) millitick(); /* start msec timer */ scStime(); pfStrtXfer(SCHAR, SNDFILE); /* (ret != 0) implies the trammission error occurred. If checkpoint protocol is available then the next transfer will restart from the breakpoint of the file, otherwise from the beginning of the file */ ret = (*Wrdata)(fp, Ofn); /* the second millitick() returns the duration between the first and second call. writes "PARTIAL FILE to the transfer log indicating a transmission error. */ statlog( "->", getfilesize(), millitick(), (ret) ? "PARTIAL FILE" : "" ); pfEndXfer(); scEtime(); (void) fclose(fp); if (ret != 0) { pfEndfile("PARTIAL FILE"); (*Turnoff)(); return(FAIL); } /* loop depending on the size of the file */ /* give an extra try for each megabyte */ /* stbuf set in fstat several lines back */ for (im = stbuf.st_size >> 10; im >= 0; --im) { if ((ret = rmesg(RQSTCMPT, msg)) == 0) break; /* got message */ } if (ret != 0) { (*Turnoff)(); return(FAIL); } goto process; } (*Turnoff)(); return(FAIL); } /* * read message * returns: * 0 -> success * FAIL -> failure */ int rmesg(c, msg) char *msg, c; { char str[50]; DEBUG(4, "rmesg - '%c' ", c); if ((*Rdmsg)(msg, Ifn) != 0) { DEBUG(4, "got %s\n", "FAIL"); (void) sprintf(str, "expected '%c' got FAIL", c); logent(str, "BAD READ"); return(FAIL); } if (c != '\0' && msg[0] != c) { DEBUG(4, "got %s\n", msg); (void) sprintf(str, "expected '%c' got %s", c, msg); logent(str, "BAD READ"); return(FAIL); } DEBUG(4, "got %s\n", msg); return(0); } /* * write a message * returns: * 0 -> ok * FAIL -> ng */ int wmesg(m, s) char *s, m; { CDEBUG(4, "wmesg '%c'", m); CDEBUG(4, "%s\n", s); return((*Wrmsg)(m, s, Ofn)); } /* * mail results of command * return: * none */ void notify(mailopt, user, msgin, sys, msgcode) char *user, *msgin, *sys; char *msgcode; { int i; char str[BUFSIZ]; char *msg; DEBUG(4,"mailopt %d, ", mailopt); DEBUG(4,"statfopt %d\n", statfopt); if (statfopt == 0 && mailopt == 0 && *msgcode == 'Y') return; if (*msgcode == 'Y') msg = "copy succeeded"; else { i = atoi(msgcode + 1); if (i < 1 || i > EM_MAX) i = 0; msg = Em_msg[i]; } if(statfopt){ stmesg(msgin, msg); return; } (void) sprintf(str, "REQUEST: %s\n(SYSTEM: %s) %s\n", msgin, sys, msg); mailst(user, msg, str, "", ""); return; } /* * local notify * return: * none */ void lnotify(user, msgin, mesg) char *user, *msgin, *mesg; { char mbuf[BUFSIZ]; if(statfopt){ stmesg(msgin, mesg); return; } (void) sprintf(mbuf, "REQUEST: %s\n(SYSTEM: %s) %s\n", msgin, Myname, mesg); mailst(user, mesg, mbuf, "", ""); return; } /*ARGSUSED*/ static void stmesg(f, m) char *f, *m; { #ifdef notdef FILE *Cf; time_t clock; long td, th, tm, ts; #endif char msg[BUFSIZ]; DEBUG(4,"STMES %s\n",mf); sprintf(msg, "STMESG - %s", mf); logent("DENIED", msg); #ifdef notdef /* * This code is a giant security hole. * No checking is done on what file is * written and chmod'ed. For now we * just ifdef this out. */ if((Cf = fopen(mf, "a+")) == NULL){ chmod(mf, PUB_FILEMODE); return; } (void) time(&clock); (void) fprintf(Cf, "uucp job: %s (%s) ", Jobid, timeStamp()); td = clock - Nstat.t_qtime; ts = td%60; td /= 60; tm = td%60; td /= 60; th = td; (void) fprintf(Cf, "(%ld:%ld:%ld)\n%s\n%s\n\n", th, tm, ts, f, m); (void) fclose(Cf); chmod(mf, PUB_FILEMODE); #endif } /* * converse with the remote machine, agree upon a * protocol (if possible) and start the protocol. * return: * SUCCESS -> successful protocol selection * FAIL -> can't find common or open failed */ int startup(void) { extern void blptcl(); extern int fptcl(); char msg[BUFSIZ], str[BUFSIZ]; Rdmsg = imsg; Wrmsg = omsg; Turnoff = turnoff; blptcl(str); if (Role == MASTER) { RMESG(SLTPTCL, msg); if ( fptcl(&msg[1], str) == FAIL) { /* no protocol match */ WMESG(USEPTCL, NO); return(FAIL); } else { /* got protocol match */ WMESG(USEPTCL, &msg[1]); return(stptcl(&msg[1])); } } else { WMESG(SLTPTCL, str); RMESG(USEPTCL, msg); if ( fptcl(&msg[1], str) == FAIL ) { return(FAIL); } else { return(stptcl(&msg[1])); } } } /* * choose a protocol from the input string (str) * and return the found letter. * Use the MASTER string (valid) for order of selection. * return: * '\0' -> no acceptable protocol * any character -> the chosen protocol */ int fptcl(str, valid) char *str, *valid; { char *l; DEBUG(9, "Slave protocol list(%s)\n", str); DEBUG(9, "Master protocol list(%s)\n", valid); for (l = valid; *l != '\0'; l++) { if ( strchr(str, *l) != NULL) { *str = *l; *(str+1) = '\0'; /* also update string with parms */ strcpy(_Protocol, findProto(_Protocol, *str)); return(SUCCESS); } } return(FAIL); } /* * build a string of the letters of the available * protocols and return the string (str). The string consists of protocols * that are specified in the Systems and Devices files. If nothing was * specified in those files, then the string is the list of protocols from * our Ptble. * * str = place to put the protocol list * length = size of buffer at str * * return: * a pointer to string (str) */ void blptcl(str) char *str; { struct Proto *p; char *validPtr; /* Build list of valid protocols. */ for (validPtr = str, p = Ptbl; (*validPtr = p->P_id) != NULLCHAR; validPtr++, p++); /* Build _Protocol */ (void) protoString(str); /* Get desired protocols. */ return; } /* * set up the six routines (Rdmg. Wrmsg, Rddata * Wrdata, Turnon, Turnoff) for the desired protocol. * returns: * SUCCESS -> ok * FAIL -> no find or failed to open */ int stptcl(c) char *c; { struct Proto *p; for (p = Ptbl; p->P_id != '\0'; p++) { if (*c == p->P_id) { /* * found protocol * set routine */ Rdmsg = p->P_rdmsg; Wrmsg = p->P_wrmsg; Rddata = p->P_rddata; Wrdata = p->P_wrdata; Turnon = p->P_turnon; Turnoff = p->P_turnoff; if ((*Turnon)() != 0) break; CDEBUG(4, "Proto started %c\n", *c); pfPtcl(c); return(SUCCESS); } } CDEBUG(4, "Proto start-fail %c\n", *c); return(FAIL); } /* * unlink D. file * returns: * none */ void unlinkdf(file) char *file; { if (strlen(file) > (size_t) 6) (void) unlink(file); return; } /* * notify receiver of arrived file * returns: * none */ void arrived(opt, file, nuser, rmtsys, rmtuser) char *file, *nuser, *rmtsys, *rmtuser; { char mbuf[200]; if (!opt) return; (void) sprintf(mbuf, "%s from %s!%s arrived\n", file, rmtsys, rmtuser); mailst(nuser, mbuf, mbuf, "", ""); return; } /* * Check to see if there is space for file */ #define FREESPACE 50 /* Minimum freespace in blocks to permit transfer */ #define FREENODES 5 /* Minimum number of inodes to permit transfer */ /*ARGSUSED*/ static int nospace(name) char *name; #ifdef NOUSTAT {return(FALSE);} #else { struct stat statb; #ifdef STATFS struct statfs statfsb; #else struct ustat ustatb; #endif if( stat(name, &statb) < 0 ) return(TRUE); #ifdef RT if( (statb.st_mode|S_IFMT) == S_IFREG || (statb.st_mode|S_IFMT) == S_IFEXT || (statb.st_mode&S_IFMT) == S_IF1EXT ) #else if( (statb.st_mode&S_IFMT) == S_IFREG ) #endif { #ifdef STATFS if( statfs(name, &statfsb)<0 ) #else if( ustat(statb.st_dev, &ustatb)<0 ) #endif return(TRUE); #ifdef STATFS /* * Use 512-byte blocks, because that's the unit "ustat" tends * to work in. */ if( ((statfsb.f_bavail*statfsb.f_bsize)/512) < FREESPACE ) #else if( ustatb.f_tfree < FREESPACE ) #endif { logent("FREESPACE IS LOW","REMOTE TRANSFER DENIED - "); return(TRUE); } #ifdef STATFS /* * The test for "> 0" is there because the @$%#@#@$ NFS * protocol doesn't pass the number of free files over the * wire, so "statfs" on an NFS file system always returns -1. */ if( statfsb.f_ffree > 0 && statfsb.f_ffree < FREENODES ) #else if( ustatb.f_tinode < FREENODES ) #endif { logent("TOO FEW INODES","REMOTE TRANSFER DENIED - "); return(TRUE); } } return(FALSE); } #endif #ifdef V7USTAT int ustat(dev, ustat) int dev; struct ustat *ustat; { FILE *dfp, *popen(); char *fval, buf[BUFSIZ]; sprintf(buf, "%s %d %d 2>&1", V7USTAT, major(dev), minor(dev)); if ((dfp = popen(buf, "r")) == NULL) return(-1); fval = fgets(buf, sizeof(buf), dfp); if (pclose(dfp) != 0 || fval == NULL || sscanf(buf, "%d %d", &ustat->f_tfree, &ustat->f_tinode) != 2) return(-1); return(0); } #endif /* V7USTAT */