cvsdist 0b77dc9
--- krb5-1.3/src/appl/gssftp/ftp/cmds.c
cvsdist 0b77dc9
+++ krb5-1.3/src/appl/gssftp/ftp/cmds.c
bd5b8f0
@@ -99,6 +99,62 @@
cvsdist 0b77dc9
 static void quote1 (char *, int, char **);
cvsdist 0b77dc9
 static char *dotrans (char *);
cvsdist 0b77dc9
 static char *domap (char *);
bd5b8f0
+static int checkglob(const char *filename, const char *pattern);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+/*
cvsdist 0b77dc9
+ * pipeprotect: protect against "special" local filenames by prepending
bd5b8f0
+ * "./". Special local filenames are "-" and any "filename" which begins
bd5b8f0
+ * with either "|" or "/".
cvsdist 0b77dc9
+ */
cvsdist 0b77dc9
+static char *pipeprotect(char *name) 
cvsdist 0b77dc9
+{
bd5b8f0
+	static char nu[MAXPATHLEN];
bd5b8f0
+	if ((name == NULL) ||
bd5b8f0
+	    ((strcmp(name, "-") != 0) && (*name != '|') && (*name != '/'))) {
cvsdist 0b77dc9
+		return name;
cvsdist 0b77dc9
+	}
cvsdist 0b77dc9
+	strcpy(nu, ".");
cvsdist 0b77dc9
+	if (*name != '/') strcat(nu, "/");
bd5b8f0
+	if (strlen(nu) + strlen(name) >= sizeof(nu)) {
bd5b8f0
+		return NULL;
bd5b8f0
+	}
cvsdist 0b77dc9
+	strcat(nu, name);
cvsdist 0b77dc9
+	return nu;
cvsdist 0b77dc9
+}
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+/*
cvsdist 0b77dc9
+ * Look for embedded ".." in a pathname and change it to "!!", printing
cvsdist 0b77dc9
+ * a warning.
cvsdist 0b77dc9
+ */
cvsdist 0b77dc9
+static char *pathprotect(char *name)
cvsdist 0b77dc9
+{
cvsdist 0b77dc9
+	int gotdots=0, i, len;
cvsdist 0b77dc9
+	
cvsdist 0b77dc9
+	/* Convert null terminator to trailing / to catch a trailing ".." */
cvsdist 0b77dc9
+	len = strlen(name)+1;
cvsdist 0b77dc9
+	name[len-1] = '/';
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+	/*
cvsdist 0b77dc9
+	 * State machine loop. gotdots is < 0 if not looking at dots,
cvsdist 0b77dc9
+	 * 0 if we just saw a / and thus might start getting dots,
cvsdist 0b77dc9
+	 * and the count of dots seen so far if we have seen some.
cvsdist 0b77dc9
+	 */
cvsdist 0b77dc9
+	for (i=0; i
cvsdist 0b77dc9
+		if (name[i]=='.' && gotdots>=0) gotdots++;
cvsdist 0b77dc9
+		else if (name[i]=='/' && gotdots<0) gotdots=0;
cvsdist 0b77dc9
+		else if (name[i]=='/' && gotdots==2) {
cvsdist 0b77dc9
+		    printf("Warning: embedded .. in %.*s (changing to !!)\n",
cvsdist 0b77dc9
+			   len-1, name);
cvsdist 0b77dc9
+		    name[i-1] = '!';
cvsdist 0b77dc9
+		    name[i-2] = '!';
cvsdist 0b77dc9
+		    gotdots = 0;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
+		else if (name[i]=='/') gotdots = 0;
cvsdist 0b77dc9
+		else gotdots = -1;
cvsdist 0b77dc9
+	}
bd5b8f0
+	name[len-1] = '\0';
cvsdist 0b77dc9
+	return name;
cvsdist 0b77dc9
+}
cvsdist 0b77dc9
 
cvsdist 0b77dc9
 /*
cvsdist 0b77dc9
  * `Another' gets another argument, and stores the new argc and argv.
bd5b8f0
@@ -844,7 +900,15 @@
cvsdist 0b77dc9
 
cvsdist 0b77dc9
 	if (argc == 2) {
cvsdist 0b77dc9
 		argc++;
cvsdist 0b77dc9
-		argv[2] = argv[1];
cvsdist 0b77dc9
+		/* 
cvsdist 0b77dc9
+		 * Protect the user from accidentally retrieving special
cvsdist 0b77dc9
+		 * local names.
cvsdist 0b77dc9
+		 */
cvsdist 0b77dc9
+		argv[2] = pipeprotect(argv[1]);
cvsdist 0b77dc9
+		if (!argv[2]) {
cvsdist 0b77dc9
+			code = -1;
cvsdist 0b77dc9
+			return 0;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
 		loc++;
cvsdist 0b77dc9
 	}
cvsdist 0b77dc9
 	if (argc < 2 && !another(&argc, &argv, "remote-file"))
bd5b8f0
@@ -1016,8 +1080,19 @@
cvsdist 0b77dc9
 			if (mapflag) {
cvsdist 0b77dc9
 				tp = domap(tp);
cvsdist 0b77dc9
 			}
cvsdist 0b77dc9
-			recvrequest("RETR", tp, cp, "w",
cvsdist 0b77dc9
-			    tp != cp || !interactive, 1);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+			/* Reject embedded ".." */
cvsdist 0b77dc9
+			tp = pathprotect(tp);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+			/* Prepend ./ to "-" or "!*" or leading "/" */
cvsdist 0b77dc9
+			tp = pipeprotect(tp);
cvsdist 0b77dc9
+			if (tp == NULL) {
cvsdist 0b77dc9
+				/* hmm... how best to handle this? */
cvsdist 0b77dc9
+				mflag = 0;
cvsdist 0b77dc9
+			} else {
cvsdist 0b77dc9
+				recvrequest("RETR", tp, cp, "w",
cvsdist 0b77dc9
+					    tp != cp || !interactive, 1);
cvsdist 0b77dc9
+			}
cvsdist 0b77dc9
 			if (!mflag && fromatty) {
cvsdist 0b77dc9
 				ointer = interactive;
cvsdist 0b77dc9
 				interactive = 1;
bd5b8f0
@@ -1045,8 +1120,8 @@
cvsdist 0b77dc9
 	static char buf[MAXPATHLEN];
cvsdist 0b77dc9
 	static FILE *ftemp = NULL;
cvsdist 0b77dc9
 	static char **args;
cvsdist 0b77dc9
-	int oldverbose, oldhash;
cvsdist 0b77dc9
-	char *cp, *rmode;
cvsdist 0b77dc9
+	int oldverbose, oldhash, badglob = 0;
cvsdist 0b77dc9
+	char *cp;
cvsdist 0b77dc9
 
cvsdist 0b77dc9
 	if (!mflag) {
cvsdist 0b77dc9
 		if (!doglob) {
bd5b8f0
@@ -1075,23 +1150,46 @@
cvsdist 0b77dc9
 			return (NULL);
cvsdist 0b77dc9
 		}
cvsdist 0b77dc9
 #else
cvsdist 0b77dc9
-		(void) strncpy(temp, _PATH_TMP, sizeof(temp) - 1);
cvsdist 0b77dc9
-		temp[sizeof(temp) - 1] = '\0';
cvsdist 0b77dc9
-		(void) mktemp(temp);
bd5b8f0
+		int fd;
bd5b8f0
+		mode_t oldumask;
cvsdist 0b77dc9
+		(void) strcpy(temp, _PATH_TMP);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+		/* libc 5.2.18 creates with mode 0666, which is dumb */
cvsdist 0b77dc9
+		oldumask = umask(077);
cvsdist 0b77dc9
+		fd = mkstemp(temp);
cvsdist 0b77dc9
+		umask(oldumask);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+		if (fd<0) {
cvsdist 0b77dc9
+			printf("Error creating temporary file, oops\n");
cvsdist 0b77dc9
+			return NULL;
cvsdist 0b77dc9
+		}
bd5b8f0
+		close(fd);
cvsdist 0b77dc9
 #endif /* !_WIN32 */
cvsdist 0b77dc9
 		oldverbose = verbose, verbose = 0;
cvsdist 0b77dc9
 		oldhash = hash, hash = 0;
cvsdist 0b77dc9
 		if (doswitch) {
cvsdist 0b77dc9
 			pswitch(!proxy);
cvsdist 0b77dc9
 		}
cvsdist 0b77dc9
-		for (rmode = "w"; *++argv != NULL; rmode = "a")
cvsdist 0b77dc9
-			recvrequest ("NLST", temp, *argv, rmode, 0, 0);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+		while (*++argv != NULL) {
cvsdist 0b77dc9
+			recvrequest ("NLST", temp, *argv, "a", 0, 0);
bd5b8f0
+			if (!checkglob(temp, *argv)) {
cvsdist 0b77dc9
+				badglob = 1;
cvsdist 0b77dc9
+				break;
cvsdist 0b77dc9
+			}
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
+
cvsdist 0b77dc9
 		if (doswitch) {
cvsdist 0b77dc9
 			pswitch(!proxy);
cvsdist 0b77dc9
 		}
cvsdist 0b77dc9
 		verbose = oldverbose; hash = oldhash;
bd5b8f0
 		ftemp = fopen(temp, "r");
bd5b8f0
 		(void) unlink(temp);
cvsdist 0b77dc9
+		if (badglob) {
cvsdist 0b77dc9
+			printf("Refusing to handle insecure file list\n");
bd5b8f0
+			fclose(ftemp);
cvsdist 0b77dc9
+			return NULL;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
 #ifdef _WIN32
cvsdist 0b77dc9
 		free(temp);
cvsdist 0b77dc9
 		temp = NULL;
bd5b8f0
@@ -1110,6 +1208,105 @@
cvsdist 0b77dc9
 	return (buf);
cvsdist 0b77dc9
 }
cvsdist 0b77dc9
 
cvsdist 0b77dc9
+/*
cvsdist 0b77dc9
+ * Check whether given pattern matches `..'
cvsdist 0b77dc9
+ * We assume only a glob pattern starting with a dot will match
cvsdist 0b77dc9
+ * dot entries on the server.
cvsdist 0b77dc9
+ */
cvsdist 0b77dc9
+static int
cvsdist 0b77dc9
+isdotdotglob(const char *pattern)
cvsdist 0b77dc9
+{
cvsdist 0b77dc9
+	int     havedot = 0;
cvsdist 0b77dc9
+	char    c;
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+	if (*pattern++ != '.')
cvsdist 0b77dc9
+		return 0;
cvsdist 0b77dc9
+	while ((c = *pattern++) != '\0' && c != '/') {
cvsdist 0b77dc9
+		if (c == '*' || c == '?')
cvsdist 0b77dc9
+			continue;
cvsdist 0b77dc9
+		if (c == '.' && havedot++)
cvsdist 0b77dc9
+			return 0;
cvsdist 0b77dc9
+	}
cvsdist 0b77dc9
+	return 1;
cvsdist 0b77dc9
+}
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+/*
cvsdist 0b77dc9
+ * This function makes sure the list of globbed files returned from
cvsdist 0b77dc9
+ * the server doesn't contain anything dangerous such as
cvsdist 0b77dc9
+ * /home/<yourname>/.forward, or ../.forward,
cvsdist 0b77dc9
+ * or |mail foe@doe 
cvsdist 0b77dc9
+ * Covered areas:
cvsdist 0b77dc9
+ *  - returned name starts with / but glob pattern doesn't
cvsdist 0b77dc9
+ *  - glob pattern starts with / but returned name doesn't
cvsdist 0b77dc9
+ *  - returned name starts with |
cvsdist 0b77dc9
+ *  - returned name contains .. in a position where glob
cvsdist 0b77dc9
+ *    pattern doesn't match ..
cvsdist 0b77dc9
+ *    I.e. foo/.* allows foo/../bar but not foo/.bar/../fly
cvsdist 0b77dc9
+ *
cvsdist 0b77dc9
+ * Note that globbed names starting with / should really be stored
cvsdist 0b77dc9
+ * under the current working directory; this is handled in mget above.
cvsdist 0b77dc9
+ *                                            --okir
cvsdist 0b77dc9
+ */
cvsdist 0b77dc9
+static int
bd5b8f0
+checkglob(const char *filename, const char *pattern)
cvsdist 0b77dc9
+{
cvsdist 0b77dc9
+	const char      *sp;
cvsdist 0b77dc9
+	char            buffer[MAXPATHLEN], dotdot[MAXPATHLEN];
cvsdist 0b77dc9
+	int             okay = 1, nrslash, initial, nr;
cvsdist 0b77dc9
+	FILE            *fp;
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+	/* Find slashes in glob pattern, and verify whether component
cvsdist 0b77dc9
+	 * matches `..'
cvsdist 0b77dc9
+	 */
cvsdist 0b77dc9
+	initial = (pattern[0] == '/');
cvsdist 0b77dc9
+	for (sp = pattern, nrslash = 0; sp != 0; sp = strchr(sp, '/')) {
cvsdist 0b77dc9
+		while (*sp == '/')
cvsdist 0b77dc9
+			sp++;
cvsdist 0b77dc9
+		if (nrslash >= MAXPATHLEN) {
cvsdist 0b77dc9
+			printf("Incredible pattern: %s\n", pattern);
cvsdist 0b77dc9
+			return 0;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
+		dotdot[nrslash++] = isdotdotglob(sp);
cvsdist 0b77dc9
+	}
cvsdist 0b77dc9
+
bd5b8f0
+	fp = fopen(filename, "r");
bd5b8f0
+	if (fp == NULL) {
bd5b8f0
+		perror("fopen");
bd5b8f0
+		return 0;
bd5b8f0
+	}
bd5b8f0
+
cvsdist 0b77dc9
+	while (okay && fgets(buffer, sizeof(buffer), fp) != NULL) {
cvsdist 0b77dc9
+		char    *sp;
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+		if ((sp = strchr(buffer, '\n')) != 0) {
cvsdist 0b77dc9
+			*sp = '\0';
cvsdist 0b77dc9
+		} else {
cvsdist 0b77dc9
+			printf("Extremely long filename from server: %s",
cvsdist 0b77dc9
+			       buffer);
cvsdist 0b77dc9
+			okay = 0;
cvsdist 0b77dc9
+			break;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
+		if (buffer[0] == '|'
cvsdist 0b77dc9
+		    || (buffer[0] != '/' && initial)
cvsdist 0b77dc9
+		    || (buffer[0] == '/' && !initial))
cvsdist 0b77dc9
+			okay = 0;
cvsdist 0b77dc9
+		for (sp = buffer, nr = 0; sp; sp = strchr(sp, '/'), nr++) {
cvsdist 0b77dc9
+			while (*sp == '/')
cvsdist 0b77dc9
+				sp++;
cvsdist 0b77dc9
+			if (sp[0] == '.' && !strncmp(sp, "../", 3)
cvsdist 0b77dc9
+			    && (nr >= nrslash || !dotdot[nr]))
cvsdist 0b77dc9
+				okay = 0;
cvsdist 0b77dc9
+		}
cvsdist 0b77dc9
+	}
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+	if (!okay)
cvsdist 0b77dc9
+		printf("Filename provided by server "
cvsdist 0b77dc9
+		       "doesn't match pattern `%s': %s\n", pattern, buffer);
cvsdist 0b77dc9
+
cvsdist 0b77dc9
+	fclose(fp);
cvsdist 0b77dc9
+	return okay;
cvsdist 0b77dc9
+}
cvsdist 0b77dc9
+
cvsdist 0b77dc9
 static char *
cvsdist 0b77dc9
 onoff(bool)
cvsdist 0b77dc9
 	int bool;