{ mailerr("551 5.3.0", "lockmailbox %s failed; error code %d %s", p, off, errno > 0 ? errstring(errno) : ""); }
errcode = "551 5.3.0";  mailerr(errcode, "lockmailbox %s failed; error code %d %s", p, off, errno > 0 ? errstring(errno) : "");
deliver(fd, name, bouncequota)	int fd;	char *name;	bool bouncequota;{	struct stat fsb;	struct stat sb;	struct passwd *pw;	char path[MAXPATHLEN];	int mbfd = -1, nr = 0, nw, off;	char *p;	off_t curoff;#ifdef CONTENTLENGTH	off_t headerbytes;	int readamount;#endif /* CONTENTLENGTH */	char biffmsg[100], buf[8*1024];	extern char *quad_to_string();	/*	**  Disallow delivery to unknown names -- special mailboxes can be	**  handled in the sendmail aliases file.	*/	if ((pw = getpwnam(name)) == NULL)	{		if (ExitVal != EX_TEMPFAIL)			ExitVal = EX_UNAVAILABLE;		if (LMTPMode)		{			if (ExitVal == EX_TEMPFAIL)				printf("451 4.3.0 cannot lookup name: %s\r\n",				       name);			else				printf("550 5.1.1 unknown name: %s\r\n", name);		}		else		{			char *errcode = NULL;			if (ExitVal == EX_TEMPFAIL)				errcode = "451 4.3.0";			else				errcode = "550 5.1.1";			mailerr(errcode, "unknown name: %s", name);		}		return;	}	endpwent();	/*	**  Keep name reasonably short to avoid buffer overruns.	**	This isn't necessary on BSD because of the proper	**	definition of snprintf(), but it can cause problems	**	on other systems.	**  Also, clear out any bogus characters.	*/	if (strlen(name) > 40)		name[40] = '\0';	for (p = name; *p != '\0'; p++)	{		if (!isascii(*p))			*p &= 0x7f;		else if (!isprint(*p))			*p = '.';	}	(void) snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);	/*	**  If the mailbox is linked or a symlink, fail.  There's an obvious	**  race here, that the file was replaced with a symbolic link after	**  the lstat returned, but before the open.  We attempt to detect	**  this by comparing the original stat information and information	**  returned by an fstat of the file descriptor returned by the open.	**	**  NB: this is a symptom of a larger problem, that the mail spooling	**  directory is writeable by the wrong users.  If that directory is	**  writeable, system security is compromised for other reasons, and	**  it cannot be fixed here.	**	**  If we created the mailbox, set the owner/group.  If that fails,	**  just return.  Another process may have already opened it, so we	**  can't unlink it.  Historically, binmail set the owner/group at	**  each mail delivery.  We no longer do this, assuming that if the	**  ownership or permissions were changed there was a reason.	**	**  XXX	**  open(2) should support flock'ing the file.	*/tryagain:#ifdef MAILLOCK	p = name;#else /* MAILLOCK */	p = path;#endif /* MAILLOCK */	if ((off = lockmbox(p)) != 0)	{		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)		{			ExitVal = EX_TEMPFAIL;			mailerr("451 4.3.0",				"lockmailbox %s failed; error code %d %s",				p, off, errno > 0 ? errstring(errno) : "");		}		else		{			mailerr("551 5.3.0",				"lockmailbox %s failed; error code %d %s",				p, off, errno > 0 ? errstring(errno) : "");		}		return;	}	if (lstat(path, &sb) < 0)	{		int save_errno;		int mode = S_IRUSR|S_IWUSR;		gid_t gid = U_GID;#ifdef MAILGID		(void) umask(0007);		gid = MAILGID;		mode |= S_IRGRP|S_IWGRP;#endif /* MAILGID */		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE,			    mode);		save_errno = errno;		if (lstat(path, &sb) < 0)		{			ExitVal = EX_CANTCREAT;			mailerr("550 5.2.0",				"%s: lstat: file changed after open", path);			goto err1;		}		if (mbfd == -1)		{			if (save_errno == EEXIST)				goto tryagain;			/* open failed, don't try again */			mailerr("450 4.2.0", "%s: %s", path,				errstring(save_errno));			goto err0;		}		else if (fchown(mbfd, U_UID, gid) < 0)		{			mailerr("451 4.3.0", "chown %u.%u: %s",				U_UID, gid, name);			goto err1;		}		else		{			/*			**  open() was successful, now close it so can			**  be opened as the right owner again.			**  Paranoia: reset mbdf since the file descriptor			**  is no longer valid; better safe than sorry.			*/			sb.st_uid = U_UID;			(void) close(mbfd);			mbfd = -1;		}	}	else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode))	{		mailerr("550 5.2.0", "%s: irregular file", path);		goto err0;	}	else if (sb.st_uid != U_UID)	{		ExitVal = EX_CANTCREAT;		mailerr("550 5.2.0", "%s: wrong ownership (%d)",			path, sb.st_uid);		goto err0;	}	/* change UID for quota checks */	if (setreuid(0, U_UID) < 0)	{		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",			U_UID, errstring(errno), getuid(), geteuid());		goto err1;	}#ifdef DEBUG	fprintf(stderr, "new euid = %d\n", geteuid());#endif /* DEBUG */	mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0);	if (mbfd < 0)	{		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));		goto err0;	}	else if (fstat(mbfd, &fsb) < 0 ||		 fsb.st_nlink != 1 ||		 sb.st_nlink != 1 ||		 !S_ISREG(fsb.st_mode) ||		 sb.st_dev != fsb.st_dev ||		 sb.st_ino != fsb.st_ino ||# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */		 sb.st_gen != fsb.st_gen ||# endif /* HAS_ST_GEN && 0 */		 sb.st_uid != fsb.st_uid)	{		ExitVal = EX_TEMPFAIL;		mailerr("550 5.2.0", "%s: fstat: file changed after open",			path);		goto err1;	}	/* Wait until we can get a lock on the file. */	if (flock(mbfd, LOCK_EX) < 0)	{		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));		goto err1;	}	/* Get the starting offset of the new message for biff. */	curoff = lseek(mbfd, (off_t)0, SEEK_END);	if (sizeof curoff > sizeof(long))		(void) snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n",				name, quad_to_string(curoff));	else		(void) snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",				name, (long) curoff);	/* Copy the message into the file. */	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1)	{		mailerr("450 4.2.0", "temporary file: %s",			errstring(errno));		goto err1;	}#ifdef DEBUG	fprintf(stderr, "before writing: euid = %d\n", geteuid());#endif /* DEBUG */#ifdef CONTENTLENGTH	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;	for (;;)	{		if (headerbytes == 0)		{			snprintf(buf, sizeof buf, "%s", ContentHdr);			nr = strlen(buf);			headerbytes = -1;			readamount = 0;		}		else if (headerbytes > sizeof(buf) || headerbytes < 0)			readamount = sizeof(buf);		else			readamount = headerbytes;		if (readamount != 0)			nr = read(fd, buf, readamount);		if (nr <= 0)			break;		if (headerbytes > 0)			headerbytes -= nr ;#else /* CONTENTLENGTH */	while ((nr = read(fd, buf, sizeof(buf))) > 0)	{#endif /* CONTENTLENGTH */		for (off = 0; off < nr; off += nw)		{			if ((nw = write(mbfd, buf + off, nr - off)) < 0)			{#ifdef EDQUOT				if (errno == EDQUOT && bouncequota)					mailerr("552 5.2.2", "%s: %s",						path, errstring(errno));				else#endif /* EDQUOT */				mailerr("450 4.2.0", "%s: %s",					path, errstring(errno));				goto err3;			}		}	}	if (nr < 0)	{		mailerr("450 4.2.0", "temporary file: %s",			errstring(errno));		goto err3;	}	/* Flush to disk, don't wait for update. */	if (fsync(mbfd) < 0)	{		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));err3:		if (setreuid(0, 0) < 0)		{#if 0			/* already printed an error above for this recipient */			(void) e_to_sys(errno);			mailerr("450 4.2.0", "setreuid(0, 0): %s",				errstring(errno));#endif /* 0 */		}#ifdef DEBUG		fprintf(stderr, "reset euid = %d\n", geteuid());#endif /* DEBUG */		(void) ftruncate(mbfd, curoff);err1:		if (mbfd >= 0)			(void) close(mbfd);err0:		unlockmbox();		return;	}	/* Close and check -- NFS doesn't write until the close. */	if (close(mbfd))	{#ifdef EDQUOT		if (errno == EDQUOT && bouncequota)			mailerr("552 5.2.2", "%s: %s", path, errstring(errno));		else#endif /* EDQUOT */		mailerr("450 4.2.0", "%s: %s", path, errstring(errno));		(void) truncate(path, curoff);	}	else		notifybiff(biffmsg);	if (setreuid(0, 0) < 0)	{		mailerr("450 4.2.0", "setreuid(0, 0): %s",			errstring(errno));		goto err0;	}#ifdef DEBUG	fprintf(stderr, "reset euid = %d\n", geteuid());#endif /* DEBUG */	unlockmbox();	if (LMTPMode)		printf("250 2.1.5 %s OK\r\n", name);}