Hi,
I started a review of ikeca.c about the use of system(3). But as there
is a lot of others place of improvement in the code, I prefer to go
step by step, and proposing patches only for one function at time.
The following patch is a review of ca_export() function.
It adds:
- xsnprintf() function: same as snprintf() but check the return, and
call errx() on error.
It corrects:
- an invalid use of err() instead of errx()
- an invalid mode in fcopy (644 instead of 0644)
- check return for strlcpy (or add (void) casting on safe use),
chmod(2), realpath(3), replace snprintf(3) by xsnprintf()...
- convert system(3) to vfork(2)/execv(2)
The reason of the use of vfork(2) instead of fork(2) is that I follow
how system(3) is implemented (with vfork the parent is suspended until
the child makes a call to execve(2) or an exit).
Comments ? OK ?
--
Sebastien Marie
Index: ikeca.c
===================================================================
RCS file: /cvs/src/usr.sbin/ikectl/ikeca.c,v
retrieving revision 1.30
diff -u -p -r1.30 ikeca.c
--- ikeca.c 16 Jan 2015 06:40:17 -0000 1.30
+++ ikeca.c 30 Jul 2015 09:39:44 -0000
@@ -18,6 +18,8 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
+
#include <stdio.h>
#include <unistd.h>
#include <err.h>
@@ -90,6 +92,20 @@ int fcopy(char *, char *, mode_t);
int rm_dir(char *);
int ca_hier(char *);
+static void
+xsnprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ if ((ret == -1) || ((size_t)ret >= size))
+ errx(1, "vsnprintf: output truncated");
+}
+
int
ca_delete(struct ca *ca)
{
@@ -550,20 +566,23 @@ ca_export(struct ca *ca, char *keyname,
struct stat st;
char *pass;
char prev[_PASSWORD_LEN + 1];
- char cmd[PATH_MAX * 2];
char oname[PATH_MAX];
char src[PATH_MAX];
char dst[PATH_MAX];
char *p;
char tpl[] = "/tmp/ikectl.XXXXXXXXXX";
- u_int i;
int fd;
+ pid_t pid;
+ int status;
+ char *argv[18];
+ int argc;
+ char *envp[2];
if (keyname != NULL) {
if (strlcpy(oname, keyname, sizeof(oname)) >= sizeof(oname))
- err(1, "name too long");
+ errx(1, "name too long");
} else {
- strlcpy(oname, "ca", sizeof(oname));
+ (void)strlcpy(oname, "ca", sizeof(oname));
}
/* colons are not valid characters in windows filenames... */
@@ -577,99 +596,227 @@ ca_export(struct ca *ca, char *keyname,
if (pass == NULL || *pass == '\0')
err(1, "password not set");
- strlcpy(prev, pass, sizeof(prev));
+ (void)strlcpy(prev, pass, sizeof(prev));
pass = getpass("Retype export passphrase:");
if (pass == NULL || strcmp(prev, pass) != 0)
errx(1, "passphrase does not match!");
}
if (keyname != NULL) {
- snprintf(cmd, sizeof(cmd), "env EXPASS=%s %s pkcs12 -export"
- " -name %s -CAfile %s/ca.crt -inkey %s/private/%s.key"
- " -in %s/%s.crt -out %s/private/%s.pfx -passout env:EXPASS"
- " -passin file:%s", pass, PATH_OPENSSL, keyname,
- ca->sslpath, ca->sslpath, keyname, ca->sslpath, keyname,
- ca->sslpath, oname, ca->passfile);
- system(cmd);
+ switch (pid = vfork()) {
+ case -1: /* error */
+ err(1, "vfork");
+
+ case 0: /* child */
+ /* XXX pass in env is unsafe */
+ if (asprintf(&envp[0], "EXPASS=%s", pass) <= 7)
+ exit(1);
+ envp[1] = NULL;
+
+ argc = 0;
+ argv[argc++] = PATH_OPENSSL;
+ argv[argc++] = "pkcs12";
+ argv[argc++] = "-export";
+ argv[argc++] = "-name";
+ argv[argc++] = keyname;
+ argv[argc++] = "-CAfile";
+ if (asprintf(&argv[argc++], "%s/ca.crt", ca->sslpath)
+ <= 8)
+ exit(1);
+ argv[argc++] = "-inkey";
+ if (asprintf(&argv[argc++], "%s/private/%s.key",
+ ca->sslpath, keyname) <= 15)
+ exit(1);
+ argv[argc++] = "-in";
+ if (asprintf(&argv[argc++], "%s/%s.crt", ca->sslpath,
+ keyname) <= 7)
+ exit(1);
+ argv[argc++] = "-out";
+ if (asprintf(&argv[argc++], "%s/private/%s.pfx",
+ ca->sslpath, oname) <= 15)
+ exit(1);
+ argv[argc++] = "-passout";
+ argv[argc++] = "env:EXPASS";
+ argv[argc++] = "-passin";
+ if (asprintf(&argv[argc++], "file:%s", ca->passfile)
+ <= 6)
+ exit(1);
+ argv[argc++] = NULL;
+
+ execve(PATH_OPENSSL, argv, envp);
+ exit(1);
+ }
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ if (status != 0)
+ errx(1, "export command failed: %s/private/%s.pfx",
+ ca->sslpath, oname);
}
- snprintf(cmd, sizeof(cmd), "env EXPASS=%s %s pkcs12 -export"
- " -caname '%s' -name '%s' -cacerts -nokeys"
- " -in %s/ca.crt -out %s/ca.pfx -passout env:EXPASS -passin file:%s",
- pass, PATH_OPENSSL, ca->caname, ca->caname, ca->sslpath,
- ca->sslpath, ca->passfile);
- system(cmd);
+
+ switch (pid = vfork()) {
+ case -1: /* error */
+ err(1, "vfork");
+
+ case 0: /* child */
+ /* XXX pass in env is unsafe */
+ if (asprintf(&envp[0], "EXPASS=%s", pass) <= 7)
+ exit(1);
+ envp[1] = NULL;
+
+ argc = 0;
+ argv[argc++] = PATH_OPENSSL;
+ argv[argc++] = "pkcs12";
+ argv[argc++] = "-export";
+ argv[argc++] = "-caname";
+ argv[argc++] = ca->caname;
+ argv[argc++] = "-name";
+ argv[argc++] = ca->caname;
+ argv[argc++] = "-cacerts";
+ argv[argc++] = "-nokeys";
+ argv[argc++] = "-in";
+ if (asprintf(&argv[argc++], "%s/ca.crt", ca->sslpath) <= 8)
+ exit(1);
+ argv[argc++] = "-out";
+ if (asprintf(&argv[argc++], "%s/ca.pfx", ca->sslpath) <= 8)
+ exit(1);
+ argv[argc++] = "-passout";
+ argv[argc++] = "env:EXPASS";
+ argv[argc++] = "-passin";
+ if (asprintf(&argv[argc++], "file:%s", ca->passfile) <= 6)
+ exit(1);
+ argv[argc++] = NULL;
+
+ execve(PATH_OPENSSL, argv, envp);
+ exit(1);
+ }
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ if (status != 0)
+ errx(1, "export command failed: %s/ca.pfx", ca->sslpath);
+
if ((p = mkdtemp(tpl)) == NULL)
err(1, "could not create temp dir");
- chmod(p, 0755);
+ if (chmod(p, 0755) != 0)
+ err(1, "chmod");
- for (i = 0; i < nitems(hier); i++) {
- strlcpy(dst, p, sizeof(dst));
- strlcat(dst, hier[i].dir, sizeof(dst));
- if (stat(dst, &st) != 0 && errno == ENOENT &&
- mkdir(dst, hier[i].mode) != 0)
- err(1, "failed to create dir %s", dst);
- }
+ ca_hier(p);
/* create a file with the address of the peer to connect to */
if (myname != NULL) {
- snprintf(dst, sizeof(dst), "%s/export/peer.txt", p);
+ xsnprintf(dst, sizeof(dst), "%s/export/peer.txt", p);
+
if ((fd = open(dst, O_WRONLY|O_CREAT, 0644)) == -1)
err(1, "open %s", dst);
write(fd, myname, strlen(myname));
close(fd);
}
- snprintf(src, sizeof(src), "%s/ca.pfx", ca->sslpath);
- snprintf(dst, sizeof(dst), "%s/export/ca.pfx", p);
- fcopy(src, dst, 0644);
-
- snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath);
- snprintf(dst, sizeof(dst), "%s/ca/ca.crt", p);
- fcopy(src, dst, 0644);
+ xsnprintf(src, sizeof(src), "%s/ca.pfx", ca->sslpath);
+ xsnprintf(dst, sizeof(dst), "%s/export/ca.pfx", p);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
+
+ xsnprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath);
+ xsnprintf(dst, sizeof(dst), "%s/ca/ca.crt", p);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
- snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath);
+ xsnprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath);
if (stat(src, &st) == 0) {
- snprintf(dst, sizeof(dst), "%s/crls/ca.crl", p);
- fcopy(src, dst, 0644);
+ xsnprintf(dst, sizeof(dst), "%s/crls/ca.crl", p);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
}
if (keyname != NULL) {
- snprintf(src, sizeof(src), "%s/private/%s.pfx", ca->sslpath,
+ xsnprintf(src, sizeof(src), "%s/private/%s.pfx", ca->sslpath,
oname);
- snprintf(dst, sizeof(dst), "%s/export/%s.pfx", p, oname);
- fcopy(src, dst, 0644);
+ xsnprintf(dst, sizeof(dst), "%s/export/%s.pfx", p, oname);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
- snprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath,
+ xsnprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath,
keyname);
- snprintf(dst, sizeof(dst), "%s/private/%s.key", p, keyname);
- fcopy(src, dst, 0600);
- snprintf(dst, sizeof(dst), "%s/private/local.key", p);
- fcopy(src, dst, 0600);
-
- snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname);
- snprintf(dst, sizeof(dst), "%s/certs/%s.crt", p, keyname);
- fcopy(src, dst, 0644);
-
- snprintf(cmd, sizeof(cmd), "%s rsa -out %s/local.pub"
- " -in %s/private/%s.key -pubout", PATH_OPENSSL, p,
- ca->sslpath, keyname);
- system(cmd);
+ xsnprintf(dst, sizeof(dst), "%s/private/%s.key", p, keyname);
+ if (fcopy(src, dst, 0600) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
+ xsnprintf(dst, sizeof(dst), "%s/private/local.key", p);
+ if (fcopy(src, dst, 0600) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
+
+ xsnprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname);
+ xsnprintf(dst, sizeof(dst), "%s/certs/%s.crt", p, keyname);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
+
+ switch (pid = vfork()) {
+ case -1: /* error */
+ err(1, "vfork");
+
+ case 0: /* child */
+ argc = 0;
+ argv[argc++] = PATH_OPENSSL;
+ argv[argc++] = "rsa";
+ argv[argc++] = "-out";
+ if (asprintf(&argv[argc++], "%s/local.pub", p) <= 11)
+ exit(1);
+ argv[argc++] = "-in";
+ if (asprintf(&argv[argc++], "%s/private/%s.key",
+ ca->sslpath, keyname) <= 15)
+ exit(1);
+ argv[argc++] = "-pubout";
+ argv[argc++] = NULL;
+
+ execv(PATH_OPENSSL, argv);
+ exit(1);
+
+ }
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ if (status != 0)
+ errx(1, "export command failed: %s/local.pub",
+ ca->sslpath);
}
if (stat(PATH_TAR, &st) == 0) {
- if (keyname == NULL)
- snprintf(cmd, sizeof(cmd), "%s -zcf %s.tgz -C %s .",
- PATH_TAR, oname, ca->sslpath);
- else
- snprintf(cmd, sizeof(cmd), "%s -zcf %s.tgz -C %s .",
- PATH_TAR, oname, p);
- system(cmd);
- snprintf(src, sizeof(src), "%s.tgz", oname);
+ switch (pid= vfork()) {
+ case -1: /* error */
+ err(1, "vfork");
+
+ case 0: /* child */
+ argc = 0;
+ argv[argc++] = PATH_TAR;
+ argv[argc++] = "-zcf";
+ if (asprintf(&argv[argc++], "%s.tgz", oname) <= 5)
+ exit(1);
+ argv[argc++] = "-C";
+ if (keyname == NULL)
+ argv[argc++] = ca->sslpath;
+ else
+ argv[argc++] = p;
+ argv[argc++] = ".";
+ argv[argc++] = NULL;
+
+ execv(PATH_TAR, argv);
+ exit(1);
+ }
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ if (status != 0)
+ errx(1, "export command failed: %s.tgz", oname);
+
+ xsnprintf(src, sizeof(src), "%s.tgz", oname);
if (realpath(src, dst) != NULL)
printf("exported files in %s\n", dst);
+ else
+ err(1, "realpath: %s", src);
}
if (stat(PATH_ZIP, &st) == 0) {
@@ -679,25 +826,47 @@ ca_export(struct ca *ca, char *keyname,
if (!strcmp(de->d_name, ".") ||
!strcmp(de->d_name, ".."))
continue;
- snprintf(src, sizeof(src), "%s/%s", EXPDIR,
+ xsnprintf(src, sizeof(src), "%s/%s", EXPDIR,
de->d_name);
- snprintf(dst, sizeof(dst), "%s/export/%s", p,
+ xsnprintf(dst, sizeof(dst), "%s/export/%s", p,
de->d_name);
- fcopy(src, dst, 644);
+ if (fcopy(src, dst, 0644) != 0)
+ errx(1, "fcopy: %s -> %s", src, dst);
}
closedir(dexp);
}
- snprintf(dst, sizeof(dst), "%s/export", p);
+ xsnprintf(dst, sizeof(dst), "%s/export", p);
if (getcwd(src, sizeof(src)) == NULL)
err(1, "could not get cwd");
if (chdir(dst) == -1)
err(1, "could not change %s", dst);
- snprintf(dst, sizeof(dst), "%s/%s.zip", src, oname);
- snprintf(cmd, sizeof(cmd), "%s -qr %s .", PATH_ZIP, dst);
- system(cmd);
+ switch (pid = vfork()) {
+ case -1: /* error */
+ err(1, "vfork");
+
+ case 0: /* child */
+ argc = 0;
+ argv[argc++] = PATH_ZIP;
+ argv[argc++] = "-qr";
+ if (asprintf(&argv[argc++], "%s/%s.zip", src, oname)
+ <= 7)
+ exit(1);
+ argv[argc++] = ".";
+ argv[argc++] = NULL;
+
+ execv(PATH_ZIP, argv);
+ exit(1);
+ }
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ if (status != 0)
+ errx(1, "export command failed: %s/%s.zip", src, oname);
+
+ xsnprintf(dst, sizeof(dst), "%s/%s.zip", src, oname);
printf("exported files in %s\n", dst);
if (chdir(src) == -1)