ncpfs (2.2.6-8) 12-572937-multiple-security-issues.patch

Summary

 sutil/ncplogin.c    |    2 
 sutil/ncpm_common.c |   46 ++++++++-
 sutil/ncpm_common.h |    3 
 sutil/ncpmount.c    |   12 ++
 sutil/ncpumount.c   |  245 +++++++++++++++++++++++++++++++++++++++++++++++++---
 5 files changed, 290 insertions(+), 18 deletions(-)

    
download this patch

Patch contents

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572937

http://seclists.org/fulldisclosure/2010/Mar/122

The ncpmount, ncpumount, and ncplogin utilities, installed as part of the ncpfs
package, contain several vulnerabilities.

1. ncpmount, ncpumount, and ncplogin are vulnerable to race conditions that
allow a local attacker to unmount arbitrary mountpoints, causing
denial-of-service, or mount Netware shares to arbitrary directories,
potentially leading to root compromise.  This issue was formerly assigned
CVE-2009-3297, but has since been re-assigned CVE-2010-0788 to avoid overlap
with related bugs in other packages.

2. ncpumount is vulnerable to an information disclosure vulnerability that
allows a local attacker to verify the existence of arbitrary files, violating
directory permissions.  This issue has been assigned CVE-2010-0790.

3. ncpmount, ncpumount, and ncplogin create lockfiles insecurely, allowing a
local attacker to leave a stale lockfile at /etc/mtab~, causing other mount
utilities to fail and creating denial-of-service conditions.  This issue has
been assigned CVE-2010-0791.

--- a/sutil/ncplogin.c	2010-03-03 16:18:59.000000000 -0500
+++ b/sutil/ncplogin.c	2010-03-03 16:17:41.000000000 -0500
@@ -934,7 +934,9 @@
 	NWDSFreeContext(ctx);
 	/* ncpmap, ncplogin must write in /etc/mtab */
 	{
+		block_sigs();
 		add_mnt_entry(mount_name, mount_point, info.flags);
+		unblock_sigs();
 	}
 	free(mount_name);
 	if (info.echo_mnt_pnt) {
--- a/sutil/ncpm_common.c	2010-03-03 16:18:59.000000000 -0500
+++ b/sutil/ncpm_common.c	2010-03-03 16:17:41.000000000 -0500
@@ -360,7 +360,7 @@
 #endif 
 
 static inline int ncpm_suser(void) {
-	return setreuid(-1, 0);
+	return setresuid(0, 0, myuid);
 }
 
 static int ncpm_normal(void) {
@@ -368,11 +368,31 @@
 	int v;
 
 	e = errno;
-	v = setreuid(-1, myuid);
+	v = setresuid(myuid, myuid, 0);
 	errno = e;
 	return v;
 }
 
+void block_sigs(void) {
+
+	sigset_t mask, orig_mask;
+	sigfillset(&mask);
+
+	if(sigprocmask(SIG_SETMASK, &mask, &orig_mask) < 0) {
+		errexit(-1, _("Blocking signals failed.\n"));
+	}
+}
+
+void unblock_sigs(void) {
+
+	sigset_t mask, orig_mask;
+	sigemptyset(&mask);
+
+	if (sigprocmask(SIG_SETMASK, &mask, &orig_mask) < 0) {
+		errexit(-1, _("Un-blocking signals failed.\n"));
+	}
+}
+
 static int proc_ncpm_mount(const char* source, const char* target, const char* filesystem, unsigned long mountflags, const void* data) {
 	int v;
 	int e;
@@ -444,7 +464,7 @@
 	}
         datav2.file_mode   = data->file_mode;
         datav2.dir_mode    = data->dir_mode;
-	err = proc_ncpm_mount(mount_name, data->mount_point, "ncpfs", flags, (void*) &datav2);
+	err = proc_ncpm_mount(mount_name, ".", "ncpfs", flags, (void*) &datav2);
 	if (err)
 		return errno;
 	return 0;
@@ -508,7 +528,7 @@
 		exit(0);	/* Should not return from process_connection */
 	}
 	close(pp[0]);
-	err=proc_ncpm_mount(mount_name, data->mount_point, "ncpfs", flags, (void*) &datav3);
+	err=proc_ncpm_mount(mount_name, ".", "ncpfs", flags, (void*) &datav3);
 	if (err) {
 		err = errno;
 		/* Mount unsuccesful so we have to kill daemon */
@@ -559,7 +579,7 @@
 		sprintf(mountopts, "version=%u,flags=%u,owner=%u,uid=%u,gid=%u,mode=%u,dirmode=%u,timeout=%u,retry=%u,wdogpid=%u,ncpfd=%u,infofd=%u",
 			NCP_MOUNT_VERSION_V5, ncpflags, data->mounted_uid, data->uid, data->gid, data->file_mode,
 			data->dir_mode, data->time_out, data->retry_count, wdog_pid, data->ncp_fd, pp[1]);
-		err=proc_ncpm_mount(mount_name, data->mount_point, "ncpfs", flags, mountopts);
+		err=proc_ncpm_mount(mount_name, ".", "ncpfs", flags, mountopts);
 	} else {
 		err=-1;
 	}
@@ -577,7 +597,7 @@
 	        datav4.file_mode   = data->file_mode;
         	datav4.dir_mode    = data->dir_mode;
 		datav4.wdog_pid	   = wdog_pid;
-		err = proc_ncpm_mount(mount_name, data->mount_point, "ncpfs", flags, (void*)&datav4);
+		err = proc_ncpm_mount(mount_name, ".", "ncpfs", flags, (void*)&datav4);
 		if (err) {
 			err = errno;
 			/* Mount unsuccesful so we have to kill daemon */
@@ -1395,6 +1415,17 @@
 }
 #endif /* MOUNT3 */
 
+static int check_name(const char *name)
+{
+	char *s;
+	for (s = "\n\t\\"; *s; s++) {
+		if (strchr(name, *s)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
 static const struct smntflags {
 	unsigned int	flag;
 	const char*	name;
@@ -1416,6 +1447,9 @@
 	int fd;
 	FILE* mtab;
 
+	if (check_name(mount_name) == -1 || check_name(mpnt) == -1)
+		errexit(107, _("Illegal character in mount entry\n"));
+
 	ment.mnt_fsname = mount_name;
 	ment.mnt_dir = mpnt;
 	ment.mnt_type = (char*)"ncpfs";
--- a/sutil/ncpm_common.h	2010-03-03 16:18:59.000000000 -0500
+++ b/sutil/ncpm_common.h	2010-03-03 16:17:41.000000000 -0500
@@ -121,6 +121,9 @@
 int proc_aftermount(const struct ncp_mount_info* info, NWCONN_HANDLE* conn);
 int proc_ncpm_umount(const char* dir);
 
+void block_sigs(void);
+void unblock_sigs(void);
+
 #define UNUSED(x)	x __attribute__((unused))
 
 #endif	/* __NCPM_COMMON_H__ */
--- a/sutil/ncpmount.c	2010-03-03 16:18:59.000000000 -0500
+++ b/sutil/ncpmount.c	2010-03-03 16:17:41.000000000 -0500
@@ -359,11 +359,17 @@
 		usage();
 		return -1;
 	}
+
 	realpath(argv[optind], mount_point);
 
-	if (stat(mount_point, &st) == -1)
+	if (chdir(mount_point))
+	{
+		errexit(31, _("Could not change directory into mount target %s: %s\n"),
+			mount_point, strerror(errno));
+	}
+	if (stat(".", &st) == -1)
 	{
-		errexit(31, _("Could not find mount point %s: %s\n"),
+		errexit(31, _("Mount point %s does not exist: %s\n"),
 			mount_point, strerror(errno));
 	}
 	if (mount_ok(&st) != 0)
@@ -714,7 +720,9 @@
 	ncp_close(conn);
 
 	if (!opt_n) {
+		block_sigs();
 		add_mnt_entry(mount_name, mount_point, info.flags);
+		unblock_sigs();
 	}
 	return 0;
 }
--- a/sutil/ncpumount.c	2010-03-03 16:18:59.000000000 -0500
+++ b/sutil/ncpumount.c	2010-03-03 16:17:41.000000000 -0500
@@ -70,13 +70,24 @@
 #include <mntent.h>
 #include <pwd.h>
 
+#include <sched.h>
+
 #include "private/libintl.h"
 
 #define _(X) X
 
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19)
+#endif
+
 static char *progname;
 static int is_ncplogout = 0;
 
+uid_t uid;
+
 static void
 usage(void)
 {
@@ -117,6 +128,40 @@
 	va_end(ap);
 }
 
+/* Mostly copied from ncpm_common.c */
+void block_sigs(void) {
+
+	sigset_t mask, orig_mask;
+	sigfillset(&mask);
+	sigdelset(&mask, SIGALRM); /* Need SIGALRM for ncpumount */
+
+	if(setresuid(0, 0, uid) < 0) {
+		eprintf("Failed to raise privileges.\n");
+		exit(-1);
+	}
+
+	if(sigprocmask(SIG_SETMASK, &mask, &orig_mask) < 0) {
+		eprintf("Blocking signals failed.\n");
+		exit(-1);
+	}
+}
+
+void unblock_sigs(void) {
+
+	sigset_t mask, orig_mask;
+	sigemptyset(&mask);
+
+	if(setresuid(uid, uid, 0) < 0) {
+		eprintf("Failed to drop privileges.\n");
+		exit(-1);
+	}
+
+	if(sigprocmask(SIG_SETMASK, &mask, &orig_mask) < 0) {
+		eprintf("Un-blocking signals failed.\n");
+		exit(-1);
+	}
+}
+
 static void alarmSignal(int sig) {
 	(void)sig;
 }
@@ -192,10 +237,13 @@
 	if (!numEntries)
 		return 0; /* don't waste time ! */
 
+	block_sigs();
+
 	while ((fd = open(MOUNTED "~", O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) {
 		struct timespec tm;
 
 		if (errno != EEXIST || retries == 0) {
+			unblock_sigs();
 			eprintf(_("Can't get %s~ lock file: %s\n"), MOUNTED, strerror(errno));
 			return 1;
 		}
@@ -206,6 +254,7 @@
 			alarm(0);
 			close(fd);
 			if (err) {
+				unblock_sigs();
 				eprintf(_("Can't lock lock file %s~: %s\n"), MOUNTED, _("Lock timed out"));
 				return 1;
 			}
@@ -223,26 +272,205 @@
 	err = __clearMtab(mount_points, numEntries);
 
 	if ((unlink(MOUNTED "~") == -1) && (err == 0)){
+		unblock_sigs();
 		eprintf(_("Can't remove %s~"), MOUNTED);
 		return 1;
 	}
+	unblock_sigs();
 	return err;
 }
 
+
+int ncp_mnt_umount(const char *abs_mnt, const char *rel_mnt)
+{
+	if (umount(rel_mnt) != 0) {
+		eprintf(_("Could not umount %s: %s\n"),
+			abs_mnt, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+
+static int check_is_mount_child(void *p)
+{
+	const char **a = p;
+	const char *last = a[0];
+	const char *mnt = a[1];
+	int res;
+	const char *procmounts = "/proc/mounts";
+	int found;
+	FILE *fp;
+	struct mntent *entp;
+
+	res = mount("", "/", "", MS_SLAVE | MS_REC, NULL);
+	if (res == -1) {
+		eprintf(_("Failed to mark mounts slave: %s\n"),
+			strerror(errno));
+		return 1;
+	}
+
+	res = mount(".", "/tmp", "", MS_BIND | MS_REC, NULL);
+	if (res == -1) {
+		eprintf(_("Failed to bind parent to /tmp: %s\n"),
+			strerror(errno));
+		return 1;
+	}
+
+	fp = setmntent(procmounts, "r");
+	if (fp == NULL) {
+		eprintf(_("Failed to open %s: %s\n"),
+			procmounts, strerror(errno));
+		return 1;
+	}
+
+	found = 0;
+	while ((entp = getmntent(fp)) != NULL) {
+		if (strncmp(entp->mnt_dir, "/tmp/", 5) == 0 &&
+		    strcmp(entp->mnt_dir + 5, last) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	endmntent(fp);
+
+	if (!found) {
+		eprintf(_("%s not mounted\n"), mnt);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int check_is_mount(const char *last, const char *mnt)
+{
+	char buf[131072];
+	pid_t pid, p;
+	int status;
+	const char *a[2] = { last, mnt };
+
+	pid = clone(check_is_mount_child, buf + 65536, CLONE_NEWNS, (void *) a);
+	if (pid == (pid_t) -1) {
+		eprintf(_("Failed to clone namespace: %s\n"),
+			strerror(errno));
+		return -1;
+	}
+	p = waitpid(pid, &status, __WCLONE);
+	if (p == (pid_t) -1) {
+		eprintf(_("Waitpid failed: %s\n"),
+			strerror(errno));
+		return -1;
+	}
+	if (!WIFEXITED(status)) {
+		eprintf(_("Child terminated abnormally (status %i)\n"),
+			status);
+		return -1;
+	}
+	if (WEXITSTATUS(status) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int chdir_to_parent(char *copy, const char **lastp, int *currdir_fd)
+{
+	char *tmp;
+	const char *parent;
+	char buf[PATH_MAX];
+	int res;
+
+	tmp = strrchr(copy, '/');
+	if (tmp == NULL || tmp[1] == '\0') {
+		eprintf(_("Internal error: invalid abs path: <%s>\n"),
+			copy);
+		return -1;
+	}
+	if (tmp != copy) {
+		*tmp = '\0';
+		parent = copy;
+		*lastp = tmp + 1;
+	} else if (tmp[1] != '\0') {
+		*lastp = tmp + 1;
+		parent = "/";
+	} else {
+		*lastp = ".";
+		parent = "/";
+	}
+	*currdir_fd = open(".", O_RDONLY);
+	if (*currdir_fd == -1) {
+		eprintf(_("Failed to open current directory: %s\n"),
+			strerror(errno));
+		return -1;
+	}
+	res = chdir(parent);
+	if (res == -1) {
+		eprintf(_("Failed to chdir to %s: %s\n"),
+			parent, strerror(errno));
+		return -1;
+	}
+	if (getcwd(buf, sizeof(buf)) == NULL) {
+		eprintf(_("Failed to obtain current directory: %s\n"),
+			strerror(errno));
+		return -1;
+	}
+	if (strcmp(buf, parent) != 0) {
+		eprintf(_("Mountpoint moved (%s -> %s)\n"),
+			parent, buf);
+		return -1;
+
+	}
+
+	return 0;
+}
+
+
+static int unmount_ncp(const char *mount_point)
+{
+	int currdir_fd = -1;
+	char *copy;
+	const char *last;
+	int res;
+
+	copy = strdup(mount_point);
+	if (copy == NULL) {
+		eprintf(_("Failed to allocate memory\n"));
+		return -1;
+	}
+	res = chdir_to_parent(copy, &last, &currdir_fd);
+	if (res == -1)
+		goto out;
+	res = check_is_mount(last, mount_point);
+	if (res == -1)
+		goto out;
+	res = ncp_mnt_umount(mount_point, last);
+
+out:
+	free(copy);
+	if (currdir_fd != -1) {
+		fchdir(currdir_fd);
+		close(currdir_fd);
+	}
+
+	return res;
+}
+
 static int
 do_umount(const char *mount_point)
 {
 	int fid = open(mount_point, O_RDONLY, 0);
 	uid_t mount_uid;
+	int res;
 
 	if (fid == -1) {
-		eprintf(_("Could not open %s: %s\n"),
-			mount_point, strerror(errno));
+		eprintf(_("Invalid or unauthorized mountpoint %s\n"),
+			mount_point);
 		return -1;
 	}
 	if (ncp_get_mount_uid(fid, &mount_uid) != 0) {
 		close(fid);
-		eprintf(_("%s probably not ncp-filesystem\n"),
+		eprintf(_("Invalid or unauthorized mountpoint %s\n"),
 			mount_point);
 		return -1;
 	}
@@ -253,12 +481,8 @@
 		return -1;
 	}
 	close(fid);
-	if (umount(mount_point) != 0) {
-		eprintf(_("Could not umount %s: %s\n"),
-			mount_point, strerror(errno));
-		return -1;
-	}
-	return 0;
+	res = unmount_ncp(mount_point);
+	return res;
 }
 
 
@@ -409,7 +633,8 @@
 	int allConns = 0;
 	const char *serverName = NULL;
 	const char *treeName = NULL;
-	uid_t uid = getuid();
+	
+	uid = getuid();
 
 	progname = strrchr(argv[0], '/');
 	if (progname) {