Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 1 Nov 1996 20:02:05 -0700 (MST)
From:      Marc Slemko <marcs@znep.com>
To:        "S(pork)" <spork@super-g.com>
Cc:        freebsd-questions@FreeBSD.org
Subject:   Re: lpr hole
Message-ID:  <Pine.BSF.3.95.961101183157.22655J-100000@alive.ampr.ab.ca>
In-Reply-To: <Pine.LNX.3.92.961030091845.12397A-100000@super-g.inch.com>

next in thread | previous in thread | raw e-mail | index | archive | help
freebsd-security is the appropriate list for such things; the problem has
been discussed there.  There is also a freebsd-security-notifications list
that is a moderated low volume announcements list, but it doesn't get as
much traffic as it should.  As usual, to subscribe send mail to
majordomo@freebsd.org with 'subscribe listname'. 

There is a fix for the lpr exploit, and other exploits in lpr.  It has
been added to the -current branch (latest development stuff; don't use
unless you know what you are doing) and -stable (few changes from 2.1.5; 
important fixes like this make it into -stable).  If you are concerned
about things like this, and have room for a full source tree, it is
probably worth keeping up to date with -stable with either CTM, sup or
CVSup. 

I am including the lpr patch (against 2.1.5) below (it doesn't just fix
the problem the script exploits, but also several others), but be warned
that there are other holes that are just as bad, most of them fixed in
-stable.

On Wed, 30 Oct 1996, S(pork) wrote:

> Hi,
> 
> I recently found an exploit for lpr that will allow root access by anyone
> with an account on the system.  As far as I know, this affects all FBSD.
> A temp fix is to chmod -s it, but I wonder if anyone has a patch for this.
> The exploit itself has been around for a while, but it seems to be
> resurfacing (as they always do) and coming into vogue...  From what I
> gather it's some sort of race/overflow thing that makes lpr make you a
> nice little root owned SUID shell.  I also have a few other little things
> I've found; is there any sort of security related list/archive for FBSD?
> CERT is so ridiculously behind on these things it's not even funny.
> 
> Curious about security,
> 
> Charles
> 

Index: lpr/lpc/cmds.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpc/cmds.c,v
retrieving revision 1.2
retrieving revision 1.2.4.1
diff -c -r1.2 -r1.2.4.1
*** cmds.c	1995/05/30 03:47:58	1.2
--- cmds.c	1996/11/01 04:56:57	1.2.4.1
***************
*** 276,282 ****
  
  	d1 = (struct dirent **)a;
  	d2 = (struct dirent **)b;
! 	if (c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3))
  		return(c1);
  	c1 = (*d1)->d_name[0];
  	c2 = (*d2)->d_name[0];
--- 276,282 ----
  
  	d1 = (struct dirent **)a;
  	d2 = (struct dirent **)b;
! 	if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)))
  		return(c1);
  	c1 = (*d1)->d_name[0];
  	c2 = (*d2)->d_name[0];
***************
*** 304,310 ****
  		SD = _PATH_DEFSPOOL;
  	printf("%s:\n", printer);
  
! 	for (lp = line, cp = SD; *lp++ = *cp++; )
  		;
  	lp[-1] = '/';
  
--- 304,310 ----
  		SD = _PATH_DEFSPOOL;
  	printf("%s:\n", printer);
  
! 	for (lp = line, cp = SD; (*lp++ = *cp++); )
  		;
  	lp[-1] = '/';
  
***************
*** 591,597 ****
  	cp1 = buf;
  	while (--argc >= 0) {
  		cp2 = *argv++;
! 		while (*cp1++ = *cp2++)
  			;
  		cp1[-1] = ' ';
  	}
--- 591,597 ----
  	cp1 = buf;
  	while (--argc >= 0) {
  		cp2 = *argv++;
! 		while ((*cp1++ = *cp2++))
  			;
  		cp1[-1] = ' ';
  	}
***************
*** 814,820 ****
  	fd = open(line, O_RDONLY);
  	if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
  		(void) close(fd);	/* unlocks as well */
! 		printf("\tno daemon present\n");
  		return;
  	}
  	(void) close(fd);
--- 814,820 ----
  	fd = open(line, O_RDONLY);
  	if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
  		(void) close(fd);	/* unlocks as well */
! 		printf("\tprinter idle\n");
  		return;
  	}
  	(void) close(fd);
Index: lpr/lpc/lpc.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpc/lpc.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.6.1
diff -c -r1.1.1.1 -r1.1.1.1.6.1
*** lpc.c	1994/05/26 05:21:55	1.1.1.1
--- lpc.c	1996/11/01 04:57:02	1.1.1.1.6.1
***************
*** 171,177 ****
  	longest = 0;
  	nmatches = 0;
  	found = 0;
! 	for (c = cmdtab; p = c->c_name; c++) {
  		for (q = name; *q == *p++; q++)
  			if (*q == 0)		/* exact match? */
  				return(c);
--- 171,177 ----
  	longest = 0;
  	nmatches = 0;
  	found = 0;
! 	for (c = cmdtab; (p = c->c_name); c++) {
  		for (q = name; *q == *p++; q++)
  			if (*q == 0)		/* exact match? */
  				return(c);
Index: lpr/lpd/lpd.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/lpd.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.6.1
diff -c -r1.1.1.1 -r1.1.1.1.6.1
*** lpd.c	1994/05/26 05:21:53	1.1.1.1
--- lpd.c	1996/11/01 04:57:09	1.1.1.1.6.1
***************
*** 176,181 ****
--- 176,182 ----
  	}
  #define	mask(s)	(1 << ((s) - 1))
  	omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+ 	(void) umask(07);
  	signal(SIGHUP, mcleanup);
  	signal(SIGINT, mcleanup);
  	signal(SIGQUIT, mcleanup);
***************
*** 190,195 ****
--- 191,197 ----
  		syslog(LOG_ERR, "ubind: %m");
  		exit(1);
  	}
+ 	(void) umask(0);
  	sigsetmask(omask);
  	FD_ZERO(&defreadfds);
  	FD_SET(funix, &defreadfds);
***************
*** 242,247 ****
--- 244,253 ----
  			domain = AF_INET, fromlen = sizeof(frominet);
  			s = accept(finet,
  			    (struct sockaddr *)&frominet, &fromlen);
+ 			if (frominet.sin_port == htons(20)) {
+ 				close(s);
+ 				continue;
+ 			}
  		}
  		if (s < 0) {
  			if (errno != EINTR)
***************
*** 459,467 ****
  	register FILE *hostf;
  	int first = 1;
  	extern char *inet_ntoa();
  
  	f->sin_port = ntohs(f->sin_port);
! 	if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
  		fatal("Malformed from address");
  
  	/* Need real hostname for temporary filenames */
--- 465,475 ----
  	register FILE *hostf;
  	int first = 1;
  	extern char *inet_ntoa();
+  	int good = 0;
  
  	f->sin_port = ntohs(f->sin_port);
!  	if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED ||
!  	    f->sin_port == htons(20))
  		fatal("Malformed from address");
  
  	/* Need real hostname for temporary filenames */
***************
*** 471,480 ****
  		fatal("Host name for your address (%s) unknown",
  			inet_ntoa(f->sin_addr));
  
! 	(void) strncpy(fromb, hp->h_name, sizeof(fromb));
  	from[sizeof(fromb) - 1] = '\0';
  	from = fromb;
  
  	hostf = fopen(_PATH_HOSTSEQUIV, "r");
  again:
  	if (hostf) {
--- 479,502 ----
  		fatal("Host name for your address (%s) unknown",
  			inet_ntoa(f->sin_addr));
  
! 	(void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1);
  	from[sizeof(fromb) - 1] = '\0';
  	from = fromb;
  
+ 	/* Check for spoof, ala rlogind */
+ 	hp = gethostbyname(fromb);
+ 	if (!hp)
+ 		fatal("hostname for your address (%s) unknown",
+ 		    inet_ntoa(f->sin_addr));
+ 	for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) {
+ 		if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr,
+ 		    sizeof(f->sin_addr)))
+ 			good = 1;
+ 	}
+ 	if (good == 0)
+ 		fatal("address for your hostname (%s) not matched",
+ 		    inet_ntoa(f->sin_addr));
+ 
  	hostf = fopen(_PATH_HOSTSEQUIV, "r");
  again:
  	if (hostf) {
***************
*** 493,507 ****
  	fatal("Your host does not have line printer access");
  	/*NOTREACHED*/
  }
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
- 
--- 515,517 ----
Index: lpr/lpd/printjob.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/printjob.c,v
retrieving revision 1.4.4.2
retrieving revision 1.4.4.3
diff -c -r1.4.4.2 -r1.4.4.3
*** printjob.c	1995/10/06 10:30:44	1.4.4.2
--- printjob.c	1996/11/01 04:57:12	1.4.4.3
***************
*** 271,276 ****
--- 271,278 ----
  			if (TR != NULL)		/* output trailer */
  				(void) write(ofd, TR, strlen(TR));
  		}
+ 		(void) close(ofd);
+ 		(void) wait(NULL);
  		(void) unlink(tempfile);
  		exit(0);
  	}
Index: lpr/lpd/recvjob.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpd/recvjob.c,v
retrieving revision 1.2
retrieving revision 1.2.4.1
diff -c -r1.2 -r1.2.4.1
*** recvjob.c	1995/05/30 03:48:01	1.2
--- recvjob.c	1996/11/01 04:57:17	1.2.4.1
***************
*** 170,176 ****
  			 * returns
  			 */
  			strcpy(cp + 6, from);
! 			strcpy(tfname, cp);
  			tfname[0] = 't';
  			if (!chksize(size)) {
  				(void) write(1, "\2", 1);
--- 170,177 ----
  			 * returns
  			 */
  			strcpy(cp + 6, from);
! 			strncpy(tfname, cp, sizeof tfname-1);
! 			tfname[sizeof tfname-1] = '\0';
  			tfname[0] = 't';
  			if (!chksize(size)) {
  				(void) write(1, "\2", 1);
***************
*** 197,203 ****
  				(void) write(1, "\2", 1);
  				continue;
  			}
! 			(void) strcpy(dfname, cp);
  			if (index(dfname, '/'))
  				frecverr("readjob: %s: illegal path name",
  					dfname);
--- 198,205 ----
  				(void) write(1, "\2", 1);
  				continue;
  			}
! 			(void) strncpy(dfname, cp, sizeof dfname-1);
! 			dfname[sizeof dfname-1] = '\0';
  			if (index(dfname, '/'))
  				frecverr("readjob: %s: illegal path name",
  					dfname);
Index: lpr/lpr/lpr.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lpr/lpr.c,v
retrieving revision 1.2.4.2
retrieving revision 1.2.4.4
diff -c -r1.2.4.2 -r1.2.4.4
*** lpr.c	1995/10/09 08:39:17	1.2.4.2
--- lpr.c	1996/11/01 04:57:36	1.2.4.4
***************
*** 111,117 ****
  static int	 test __P((char *));
  static int	 checkwriteperm __P((char*, char *));
  
! void
  main(argc, argv)
  	int argc;
  	char *argv[];
--- 111,117 ----
  static int	 test __P((char *));
  static int	 checkwriteperm __P((char*, char *));
  
! int
  main(argc, argv)
  	int argc;
  	char *argv[];
***************
*** 288,294 ****
  	/*
  	 * Check to make sure queuing is enabled if userid is not root.
  	 */
! 	(void) sprintf(buf, "%s/%s", SD, LO);
  	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
  		fatal2("Printer queue is disabled");
  	/*
--- 288,294 ----
  	/*
  	 * Check to make sure queuing is enabled if userid is not root.
  	 */
! 	(void) snprintf(buf, sizeof(buf), "%s/%s", SD, LO);
  	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
  		fatal2("Printer queue is disabled");
  	/*
***************
*** 331,337 ****
  			continue;	/* file unreasonable */
  
  		if (sflag && (cp = linked(arg)) != NULL) {
! 			(void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino);
  			card('S', buf);
  			if (format == 'p')
  				card('T', title ? title : arg);
--- 331,338 ----
  			continue;	/* file unreasonable */
  
  		if (sflag && (cp = linked(arg)) != NULL) {
! 			(void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev,
! 				statb.st_ino);
  			card('S', buf);
  			if (format == 'p')
  				card('T', title ? title : arg);
***************
*** 349,360 ****
  			printf("%s: %s: not linked, copying instead\n", name, arg);
  		if ((i = open(arg, O_RDONLY)) < 0) {
  			printf("%s: cannot open %s\n", name, arg);
! 			continue;
  		}
- 		copy(i, arg);
- 		(void) close(i);
- 		if (f && unlink(arg) < 0)
- 			printf("%s: %s: not removed\n", name, arg);
  	}
  
  	if (nact) {
--- 350,361 ----
  			printf("%s: %s: not linked, copying instead\n", name, arg);
  		if ((i = open(arg, O_RDONLY)) < 0) {
  			printf("%s: cannot open %s\n", name, arg);
! 		} else {
! 			copy(i, arg);
! 			(void) close(i);
! 			if (f && unlink(arg) < 0)
! 				printf("%s: %s: not removed\n", name, arg);
  		}
  	}
  
  	if (nact) {
***************
*** 444,450 ****
  	static char buf[BUFSIZ];
  
  	if (*file != '/') {
! 		if (getwd(buf) == NULL)
  			return(NULL);
  		while (file[0] == '.') {
  			switch (file[1]) {
--- 445,451 ----
  	static char buf[BUFSIZ];
  
  	if (*file != '/') {
! 		if (getcwd(buf,sizeof(buf)) == NULL)
  			return(NULL);
  		while (file[0] == '.') {
  			switch (file[1]) {
***************
*** 481,487 ****
  	register int len = 2;
  
  	*p1++ = c;
! 	while ((c = *p2++) != '\0') {
  		*p1++ = (c == '\n') ? ' ' : c;
  		len++;
  	}
--- 482,488 ----
  	register int len = 2;
  
  	*p1++ = c;
! 	while ((c = *p2++) != '\0' && len < sizeof(buf)) {
  		*p1++ = (c == '\n') ? ' ' : c;
  		len++;
  	}
***************
*** 691,697 ****
  	char buf[BUFSIZ];
  	char *lmktemp();
  
! 	(void) sprintf(buf, "%s/.seq", SD);
  	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
  		printf("%s: cannot create %s\n", name, buf);
  		exit(1);
--- 692,698 ----
  	char buf[BUFSIZ];
  	char *lmktemp();
  
! 	(void) snprintf(buf, sizeof(buf), "%s/.seq", SD);
  	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
  		printf("%s: cannot create %s\n", name, buf);
  		exit(1);
***************
*** 715,721 ****
  	inchar = strlen(SD) + 3;
  	n = (n + 1) % 1000;
  	(void) lseek(fd, (off_t)0, 0);
! 	sprintf(buf, "%03d\n", n);
  	(void) write(fd, buf, strlen(buf));
  	(void) close(fd);	/* unlocks as well */
  }
--- 716,722 ----
  	inchar = strlen(SD) + 3;
  	n = (n + 1) % 1000;
  	(void) lseek(fd, (off_t)0, 0);
! 	snprintf(buf, sizeof(buf), "%03d\n", n);
  	(void) write(fd, buf, strlen(buf));
  	(void) close(fd);	/* unlocks as well */
  }
***************
*** 732,738 ****
  
  	if ((s = malloc(len)) == NULL)
  		fatal2("out of memory");
! 	(void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
  	return(s);
  }
  
--- 733,739 ----
  
  	if ((s = malloc(len)) == NULL)
  		fatal2("out of memory");
! 	(void) snprintf(s, len, "%s/%sA%03d%s", SD, id, num, host);
  	return(s);
  }
  
Index: lpr/lptest/lptest.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/lptest/lptest.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.6.1
diff -c -r1.1.1.1 -r1.1.1.1.6.1
*** lptest.c	1994/05/26 05:21:56	1.1.1.1
--- lptest.c	1996/11/01 04:57:50	1.1.1.1.6.1
***************
*** 48,54 ****
  /*
   * lptest -- line printer test program (and other devices).
   */
! void
  main(argc, argv)
  	int argc;
  	char **argv;
--- 48,54 ----
  /*
   * lptest -- line printer test program (and other devices).
   */
! int
  main(argc, argv)
  	int argc;
  	char **argv;
Index: lpr/pac/pac.c
===================================================================
RCS file: /usr/cvs/src/usr.sbin/lpr/pac/pac.c,v
retrieving revision 1.2.4.1
retrieving revision 1.2.4.2
diff -c -r1.2.4.1 -r1.2.4.2
*** pac.c	1995/08/26 11:50:53	1.2.4.1
--- pac.c	1996/11/01 04:57:52	1.2.4.2
***************
*** 98,104 ****
  static int	qucmp __P((const void *, const void *));
  static void	rewrite __P((void));
  
! void
  main(argc, argv)
  	int argc;
  	char **argv;
--- 98,104 ----
  static int	qucmp __P((const void *, const void *));
  static void	rewrite __P((void));
  
! int
  main(argc, argv)
  	int argc;
  	char **argv;




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.3.95.961101183157.22655J-100000>