fix handling of consecutive optional arguments

The C library strtok function will skip blank tokens.  e.g. "a;;b"
will yield ["a", "b"], not ["a", "", "b"].  This affects some of our
command line options (like -k and -b), so switch them to our own
tokenize function which will yield empty strings as needed.

This also lets us make sure the user doesn't pass too many arguments.
Currently we were accepting (and ignoring the extra parts of) options
like -b /var,/var,1,foo,bar,fat,cat.  This would be treated like we
passed in -b /var,/var,1.

Bug: None
Test: `minijail0 --profile minimalistic-mountns -b /dev/log,,1 /bin/ls /dev/log` works

Change-Id: I653deb59ac8e6b1df108e1062a87dd87e7cb8a17
diff --git a/minijail0.1 b/minijail0.1
index cf2ccee..52f98e2 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -64,8 +64,9 @@
 \fB-I\fR
 Run \fIprogram\fR as init (pid 1) inside a new pid namespace (implies \fB-p\fR).
 .TP
-\fB-k <src>,<dest>,<type>[,<flags>]\fR
-Mount \fIsrc\fR, a \fItype\fR filesystem, into the chroot directory at \fIdest\fR, with optional \fIflags\fR.
+\fB-k <src>,<dest>,<type>[,<flags>[,<data>]]\fR
+Mount \fIsrc\fR, a \fItype\fR filesystem, into the chroot directory at \fIdest\fR,
+with optional \fIflags\fR and optional \fIdata\fR (see \fBmount\fR(2)).
 If the mount is not a pseudo filesystem (e.g. proc or sysfs), \fIsrc\fR path
 must be an absolute path (e.g. \fI/dev/sda1\fR and not \fIsda1\fR).
 If the destination does not exist, it will be created as a directory.
diff --git a/minijail0.c b/minijail0.c
index 32995ab..425fbf0 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -92,16 +92,18 @@
 
 static void add_binding(struct minijail *j, char *arg)
 {
-	char *src = strtok(arg, ",");
-	char *dest = strtok(NULL, ",");
-	char *flags = strtok(NULL, ",");
-	if (!src) {
+	char *src = tokenize(&arg, ",");
+	char *dest = tokenize(&arg, ",");
+	char *flags = tokenize(&arg, ",");
+	if (!src || src[0] == '\0' || arg != NULL) {
 		fprintf(stderr, "Bad binding: %s %s\n", src, dest);
 		exit(1);
 	}
-	if (!dest)
+	if (dest == NULL || dest[0] == '\0')
 		dest = src;
-	if (minijail_bind(j, src, dest, flags ? atoi(flags) : 0)) {
+	if (flags == NULL || flags[0] == '\0')
+		flags = "0";
+	if (minijail_bind(j, src, dest, atoi(flags))) {
 		fprintf(stderr, "minijail_bind failed.\n");
 		exit(1);
 	}
@@ -109,10 +111,11 @@
 
 static void add_rlimit(struct minijail *j, char *arg)
 {
-	char *type = strtok(arg, ",");
-	char *cur = strtok(NULL, ",");
-	char *max = strtok(NULL, ",");
-	if (!type || !cur || !max) {
+	char *type = tokenize(&arg, ",");
+	char *cur = tokenize(&arg, ",");
+	char *max = tokenize(&arg, ",");
+	if (!type || type[0] == '\0' || !cur || cur[0] == '\0' ||
+	    !max || max[0] == '\0' || arg != NULL) {
 		fprintf(stderr, "Bad rlimit '%s'.\n", arg);
 		exit(1);
 	}
@@ -125,12 +128,13 @@
 
 static void add_mount(struct minijail *j, char *arg)
 {
-	char *src = strtok(arg, ",");
-	char *dest = strtok(NULL, ",");
-	char *type = strtok(NULL, ",");
-	char *flags = strtok(NULL, ",");
-	char *data = strtok(NULL, ",");
-	if (!src || !dest || !type) {
+	char *src = tokenize(&arg, ",");
+	char *dest = tokenize(&arg, ",");
+	char *type = tokenize(&arg, ",");
+	char *flags = tokenize(&arg, ",");
+	char *data = tokenize(&arg, ",");
+	if (!src || src[0] == '\0' || !dest || dest[0] == '\0' ||
+	    !type || type[0] == '\0' || arg != NULL) {
 		fprintf(stderr, "Bad mount: %s %s %s\n", src, dest, type);
 		exit(1);
 	}
@@ -296,7 +300,7 @@
 	/* clang-format off */
 	printf("Usage: %s [-dGhHiIKlLnNprRstUvyYz]\n"
 	       "  [-a <table>]\n"
-	       "  [-b <src>[,<dest>[,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>][,<data>]]\n"
+	       "  [-b <src>[,<dest>[,<writeable>]]] [-k <src>,<dest>,<type>[,<flags>[,<data>]]]\n"
 	       "  [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
 	       "  [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*] [--profile <name>]\n"
 	       "  [-R <type,cur,max>] [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"