diff options
Diffstat (limited to 'open-vm-tools/guestproxycerttool/cert_util.c')
-rw-r--r-- | open-vm-tools/guestproxycerttool/cert_util.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/open-vm-tools/guestproxycerttool/cert_util.c b/open-vm-tools/guestproxycerttool/cert_util.c new file mode 100644 index 00000000..ae0e4785 --- /dev/null +++ b/open-vm-tools/guestproxycerttool/cert_util.c @@ -0,0 +1,427 @@ +/********************************************************* + * Copyright (C) 2014-2015 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +/* + * certUtil.c -- + * + * Utilities to manage the certificates. + */ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <ctype.h> +#include "cert_util.h" + +/* + *---------------------------------------------------------------------- + * + * CompareFile -- + * + * Check if two files are the same. + * + * Results: + * TRUE if file comparison is performed successfully, otherwise + * FALSE. When the returned value is TRUE, 'same' is TRUE if two + * input files are the same, otherwise FALSE. When the returned + * value is FALSE, 'same' is not defined. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static gboolean +CompareFile(const gchar *fname1, // IN + const gchar *fname2, // IN + gboolean *same) // OUT +{ + gsize num; + gboolean ret = FALSE; + GMappedFile *m1; + GMappedFile *m2 = NULL; + GError *error = NULL; + + m1 = g_mapped_file_new(fname1, FALSE, &error); + if (m1 == NULL) { + Error("Unable to map %s: %s.\n", fname1, error->message); + goto exit; + } + + m2 = g_mapped_file_new(fname2, FALSE, &error); + if (m2 == NULL) { + Error("Unable to map %s: %s.\n", fname2, error->message); + goto exit; + } + + ret = TRUE; + *same = FALSE; + + num = g_mapped_file_get_length(m1); + if (g_mapped_file_get_length(m2) == num) { + if (num) { + if (memcmp(g_mapped_file_get_contents(m1), + g_mapped_file_get_contents(m2), num) == 0) { + *same = TRUE; + } + } else { + /* Two empty files */ + *same = TRUE; + } + } + +exit: + g_clear_error(&error); + if (m1) { + g_mapped_file_unref(m1); + } + if (m2) { + g_mapped_file_unref(m2); + } + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * CertUtil_CreateCertFileName -- + * + * A convenient function to make up the certificate file name based + * on the supplied guest proxy certificate store (certDir), subject + * name hash (hash), and certificate version (version). + * + * Results: + * Return the full path name of the certificate. Callers should free + * the returned string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +gchar * +CertUtil_CreateCertFileName(const gchar *certDir, // IN + const gchar *hash, // IN + int version) // IN +{ + gchar *ret; + gchar *tmp; + + tmp = g_strdup_printf("%s.%d", hash, version); + ret = g_build_filename(certDir, tmp, NULL); + + g_free(tmp); + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * IntCmp -- + * + * This is an integer comparator, which is used to sort a list + * with ascending order. + * + * Results: + * The difference of ga and gb. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static gint +IntCmp(gconstpointer ga, // IN + gconstpointer gb) // IN +{ + gint a = GPOINTER_TO_INT(ga); + gint b = GPOINTER_TO_INT(gb); + + return (a - b); +} + + +/* + *---------------------------------------------------------------------- + * + * MatchFile -- + * + * Scan each file at the directory and collect file extensions of + * matched files. Sort the file extension list in ascending order. + * + * Results: + * Return a list of file extensions, their file names matching the + * regular expression. These extensions are file version numbers. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static GList * +MatchFile(GDir *dir, // IN + GRegex *regExpr) // IN +{ + const gchar *fn, *cp; + GList *list = NULL; + + while ((fn = g_dir_read_name(dir)) != NULL) { + if (g_regex_match(regExpr, fn, 0, NULL)) { + + cp = strrchr(fn, '.'); + list = g_list_prepend(list, GINT_TO_POINTER(atoi(cp + 1))); + } + } + + list = g_list_sort(list, IntCmp); + + return list; +} + + +/* + *---------------------------------------------------------------------- + * + * SearchFile -- + * + * Search files with pattern (<fname>.[0-9]+) at a directory <path>. + * + * Results: + * Return TRUE if file search is performed successfully, otherwise + * FALSE. When the returned value is TRUE, 'list' is set to include + * a list of file extensions matching the pattern. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static gboolean +SearchFile(const gchar *path, // IN + const gchar *fname, // IN + GList **list) // OUT +{ + gboolean ret = FALSE; + gchar *pattern; + GRegex *regExpr; + GDir *dir = NULL; + GError *error = NULL; + + pattern = g_strdup_printf("%s.[0-9]+", fname); + regExpr = g_regex_new(pattern, 0, 0, &error); + if (!regExpr) { + Error("Failed to compile %s: %s.\n", pattern, error->message); + goto exit; + } + + dir = g_dir_open(path, 0, &error); + if (!dir) { + Error("Failed to open %s: %s.\n", path, error->message); + goto exit; + } + + *list = MatchFile(dir, regExpr); + ret = TRUE; + +exit: + g_free(pattern); + g_clear_error(&error); + if (dir) { + g_dir_close(dir); + } + if (regExpr) { + g_regex_unref(regExpr); + } + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * CertUtil_FindCert -- + * + * From the trusted certificate directory (certDir), check if + * there is any certificate file matching the contents of the + * supplied one. In general, certificate files are saved in the + * directory by the format of <hash>.[0-9]+. + * + * Results: + * Return TRUE if the function is successfully executed. Otherwise + * FALSE. When return TRUE, 'num' is set to the version of matching + * certificate file or -1 if no matching. For 'last', it is set to + * the highest version, or -1 if the store has no certificate file. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +gboolean +CertUtil_FindCert(const gchar *certFile, // IN + const gchar *certDir, // IN + const gchar *hash, // IN + int *num, // OUT + int *last) // OUT +{ + gboolean ret = FALSE; + const GList *node; + GList *list = NULL; + gchar *path = NULL; + + *last = *num = -1; + if (!SearchFile(certDir, hash, &list)) { + goto exit; + } + + ret = TRUE; + if (!list) { + goto exit; + } + + /* *last = the highest file version */ + node = g_list_last(list); + *last = GPOINTER_TO_INT(node->data); + + for (node = g_list_first(list); node; node = g_list_next(node)) { + gboolean same = FALSE; + int ext = GPOINTER_TO_INT(node->data); + + g_free(path); + path = CertUtil_CreateCertFileName(certDir, hash, ext); + + if (!CompareFile(certFile, path, &same)) { + ret = FALSE; + goto exit; + } + + if (same) { + *num = ext; + break; + } + } + +exit: + g_free(path); + if (list) { + g_list_free(list); + } + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * CertUtil_GetToolDir -- + * + * Get the VMware tool installation directory. + * + * Results: + * The VMware tool installation directory. Callers should not free + * the returned string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +const gchar * +CertUtil_GetToolDir(void) +{ + static gchar *path = NULL; + + if (!path) { + path = g_build_filename(G_DIR_SEPARATOR_S, "etc", "vmware-tools", NULL); + } + + return path; +} + + +/* + *---------------------------------------------------------------------- + * + * CertUtil_CopyFile -- + * + * Copy a file from source to destination. + * + * Results: + * TRUE if success, otherwise FALSE. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +gboolean +CertUtil_CopyFile(const gchar *src, // IN + const gchar *dst) // IN +{ + gsize length; + gboolean ret = FALSE; + GMappedFile *smap; + GError *error = NULL; + FILE *file = NULL; + const gchar *content; + + smap = g_mapped_file_new(src, FALSE, &error); + if (!smap) { + Error("Unable to map %s: %s.\n", src, error->message); + goto exit; + } + + file = fopen(dst, "w"); + if (!file) { + Error("Failed to open %s: %s.\n", dst, strerror(errno)); + goto exit; + } + + length = g_mapped_file_get_length(smap); + content = g_mapped_file_get_contents(smap); + if (fwrite(content, 1, length, file) < length) { + Error("Failed to copy %s to %s: %s.\n", src, dst, strerror(errno)); + goto exit; + } + + ret = TRUE; + +exit: + g_clear_error(&error); + if (smap) { + g_mapped_file_unref(smap); + } + if (file) { + fclose(file); + } + + return ret; +} |