summaryrefslogtreecommitdiff
path: root/open-vm-tools/vgauth/cli/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'open-vm-tools/vgauth/cli/main.c')
-rw-r--r--open-vm-tools/vgauth/cli/main.c661
1 files changed, 661 insertions, 0 deletions
diff --git a/open-vm-tools/vgauth/cli/main.c b/open-vm-tools/vgauth/cli/main.c
new file mode 100644
index 00000000..7755e1c6
--- /dev/null
+++ b/open-vm-tools/vgauth/cli/main.c
@@ -0,0 +1,661 @@
+/*********************************************************
+ * Copyright (C) 2011-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.
+ *
+ *********************************************************/
+
+/**
+ * @file main.c
+ *
+ * The GuestAuth certificate manipulation command line tool.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#include <errno.h>
+#endif
+#include <glib.h>
+
+#include "VGAuthBasicDefs.h"
+#include "VGAuthAlias.h"
+#include "VGAuthCommon.h"
+#include "VGAuthError.h"
+#include "VGAuthLog.h"
+#include "VGAuthUtil.h"
+#define VMW_TEXT_DOMAIN "VGAuthCli"
+#include "i18n.h"
+#include "prefs.h"
+
+static const gchar *appName;
+
+static gboolean verbose = FALSE;
+
+
+/*
+ ******************************************************************************
+ * Usage -- */ /**
+ *
+ * Usage message for CLI
+ *
+ * @param[in] optContext The GOptionContext for generating the message
+ *
+ ******************************************************************************
+ */
+
+static void
+Usage(GOptionContext *optContext)
+{
+ gchar *usage;
+
+ usage = g_option_context_get_help(optContext, TRUE, NULL);
+ g_printerr("%s", usage);
+ g_free(usage);
+ exit(-1);
+}
+
+
+/*
+ ******************************************************************************
+ * CliLog -- */ /**
+ *
+ * Error message logging function for the CLI.
+ *
+ * @param[in] logDomain The glib logging domain, which is set by the
+ * various glib components and vgauth itself.
+ * @param[in] logLevel The severity of the message.
+ * @param[in] msg The error message.
+ * @param[in] userData Any userData specified in the call to
+ * VGAuth_SetLogHandler()
+ *
+ ******************************************************************************
+ */
+
+static void
+CliLog(const char *logDomain,
+ int logLevel,
+ const char *msg,
+ void *userData)
+{
+ // ignore all but errors
+ if (logLevel & G_LOG_LEVEL_WARNING) {
+ g_printerr("%s[%d]: %s", logDomain, logLevel, msg);
+#ifdef VMX86_DEBUG
+ } else {
+ fprintf(stderr, "Dropping message %s[%d]: %s", logDomain, logLevel, msg);
+#endif
+ }
+}
+
+
+/*
+ ******************************************************************************
+ * SubjectName -- */ /**
+ *
+ * Returns the name value for a subject, or <ANY>.
+ *
+ * @param[in] s The VGAuthSubject.
+ *
+ ******************************************************************************
+ */
+
+static const gchar *
+SubjectName(VGAuthSubject *s)
+{
+ if (s->type == VGAUTH_SUBJECT_NAMED) {
+ return s->val.name;
+ } else {
+ return SU_(name.any, "<ANY>");
+ }
+}
+
+
+/*
+ ******************************************************************************
+ * CliLoadPemFile -- */ /**
+ *
+ * Loads a PEM certificate from a file. The caller should g_free() the
+ * return value when finished.
+ *
+ * @param[in] fileName The filename of the cert in PEM format.
+ *
+ * @return The contents of the certificate file.
+ *
+ ******************************************************************************
+ */
+
+static gchar *
+CliLoadPemFILE(const gchar *fileName)
+{
+ gchar *contents = NULL;
+ gsize fileSize;
+ GError *gErr = NULL;
+
+ /*
+ * XXX
+ *
+ * Might be nice for this to handle stdin. Either a NULL
+ * filename or "-" ?
+ */
+ if (!g_file_get_contents(fileName, &contents, &fileSize, &gErr)) {
+ g_printerr(SU_(loadfile.fail,
+ "%s: Unable to read PEM file '%s'\n"),
+ appName, gErr->message);
+ g_error_free(gErr);
+ }
+
+ return contents;
+}
+
+
+/*
+ ******************************************************************************
+ * CliAddAlias -- */ /**
+ *
+ * Adds a certficate and subject for the user.
+ *
+ * @param[in] ctx The VGAuthContext.
+ * @param[in] userName The user whose store is being changed.
+ * @param[in] subject The associated subject name.
+ * @param[in] pemFileName The filename of the cert in PEM format.
+ * @param[in] addMapped Set if a link is also to be added to
+ * the mapping file.
+ * @param[in] comment The comment.
+ *
+ * @return VGAUTH_E_OK on success, VGAuthError on failure
+ *
+ ******************************************************************************
+ */
+
+static VGAuthError
+CliAddAlias(VGAuthContext *ctx,
+ const char *userName,
+ const char *subject,
+ const char *pemFilename,
+ gboolean addMapped,
+ const char *comment)
+{
+ gchar *pemCert = NULL;
+ VGAuthError err;
+ VGAuthAliasInfo ai;
+
+ pemCert = CliLoadPemFILE(pemFilename);
+ if (NULL == pemCert) {
+ return VGAUTH_E_INVALID_CERTIFICATE;
+ }
+
+ /*
+ * The 'comment' cmdline arg is optional, but the underlying API needs
+ * a real value.
+ */
+ ai.comment = (comment) ? (char *) comment : "";
+
+ ai.subject.type = VGAUTH_SUBJECT_NAMED;
+ ai.subject.val.name = (char *) subject;
+
+ err = VGAuth_AddAlias(ctx, userName, addMapped, pemCert, &ai, 0, NULL);
+ if (VGAUTH_E_OK != err) {
+ g_printerr(SU_(addsubj.fail,
+ "%s: Failed to add alias for user '%s': %s.\n"),
+ appName, userName, VGAuth_GetErrorText(err, NULL));
+ } else if (verbose) {
+ g_print(SU_(addsubj.success, "%s: alias added\n"), appName);
+ }
+
+ g_free(pemCert);
+
+ return err;
+}
+
+
+/*
+ ******************************************************************************
+ * CliRemoveAlias -- */ /**
+ *
+ * Removes a certficate for the user.
+ *
+ * @param[in] ctx The VGAuthContext.
+ * @param[in] userName The user whose store is being changed.
+ * @param[in] subject The associated subject.
+ * @param[in] pemFileName The filename of the cert in PEM format.
+ *
+ * @return VGAUTH_E_OK on success, VGAuthError on failure
+ *
+ ******************************************************************************
+ */
+
+static VGAuthError
+CliRemoveAlias(VGAuthContext *ctx,
+ const char *userName,
+ const char *subject,
+ const char *pemFilename)
+{
+ VGAuthError err;
+ gchar *pemCert = NULL;
+ VGAuthSubject subj;
+
+ pemCert = CliLoadPemFILE(pemFilename);
+ if (NULL == pemCert) {
+ return VGAUTH_E_INVALID_CERTIFICATE;
+ }
+
+ if (subject) {
+ subj.val.name = (char *) subject;
+ subj.type = VGAUTH_SUBJECT_NAMED;
+ err = VGAuth_RemoveAlias(ctx, userName, pemCert, &subj, 0, NULL);
+ } else {
+ err = VGAuth_RemoveAliasByCert(ctx, userName, pemCert, 0, NULL);
+ }
+
+ if (VGAUTH_E_OK != err) {
+ g_printerr(SU_(removesubj.fail,
+ "%s: Failed to remove alias for user '%s': %s.\n"),
+ appName, userName, VGAuth_GetErrorText(err, NULL));
+ } else if (verbose) {
+ g_print(SU_(removesubj.success, "%s: alias removed\n"), appName);
+ }
+
+ g_free(pemCert);
+
+ return err;
+}
+
+
+/*
+ ******************************************************************************
+ * CliList -- */ /**
+ *
+ * List all UserAliases for a user.
+ *
+ * @param[in] ctx The VGAuthContext.
+ * @param[in] userName The user whose store is being queried.
+ *
+ * @return VGAUTH_E_OK on success, VGAuthError on failure
+ *
+ ******************************************************************************
+ */
+
+static VGAuthError
+CliList(VGAuthContext *ctx,
+ const char *userName)
+{
+ VGAuthError err;
+ int num;
+ int i;
+ int j;
+ VGAuthUserAlias *uaList;
+
+ err = VGAuth_QueryUserAliases(ctx, userName, 0, NULL, &num, &uaList);
+ if (VGAUTH_E_OK != err) {
+ g_printerr(SU_(list.error,
+ "%s: Failed to list aliases for user '%s': %s.\n"),
+ appName, userName, VGAuth_GetErrorText(err, NULL));
+ return err;
+ }
+
+ if (verbose) {
+ g_print(SU_(list.count, "%s Found %d aliases for user '%s'\n"),
+ appName, num, userName);
+ }
+
+ for (i = 0; i < num; i++) {
+ g_print("%s\n", uaList[i].pemCert);
+ for (j = 0; j < uaList[i].numInfos; j++) {
+ g_print("\t%s: %s %s: %s\n",
+ SU_(list.subject, "Subject"),
+ SubjectName(&(uaList[i].infos[j].subject)),
+ SU_(list.comment, "Comment"),
+ uaList[i].infos[j].comment);
+ }
+ }
+ VGAuth_FreeUserAliasList(num, uaList);
+
+ return err;
+}
+
+
+/*
+ ******************************************************************************
+ * CliListMapped -- */ /**
+ *
+ * List all IdProviders in the mapping file.
+ *
+ * @param[in] ctx The VGAuthContext.
+ *
+ * @return VGAUTH_E_OK on success, VGAuthError on failure
+ *
+ ******************************************************************************
+ */
+
+static VGAuthError
+CliListMapped(VGAuthContext *ctx)
+{
+ VGAuthError err;
+ int num;
+ VGAuthMappedAlias *maList;
+ int i;
+ int j;
+
+ err = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
+ if (VGAUTH_E_OK != err) {
+ g_printerr(SU_(listmapped.error,
+ "%s: Failed to list mapped aliases: %s.\n"),
+ appName, VGAuth_GetErrorText(err, NULL));
+ return err;
+ }
+
+ if (verbose) {
+ g_print(SU_(listmapped.count, "%s Found %d mapped aliases\n"),
+ appName, num);
+ }
+ for (i = 0; i < num; i++) {
+ g_print("%s\n%s:%s\n", maList[i].pemCert,
+ SU_(listmapped.username, "Username"),
+ maList[i].userName);
+ for (j = 0; j < maList[i].numSubjects; j++) {
+ g_print("\t%s: %s\n",
+ SU_(listmapped.subject, "Subject"),
+ SubjectName(&(maList[i].subjects[j])));
+
+ }
+ }
+ VGAuth_FreeMappedAliasList(num, maList);
+
+ return err;
+}
+
+
+/*
+ ******************************************************************************
+ * mainRun -- */ /**
+ *
+ * Initializes and parses commandline args.
+ *
+ * @param[in] argc Number of command line arguments.
+ * @param[in] argv The command line arguments.
+ *
+ * @return 0 if the operation ran successfully, -1 if there was an error during
+ * execution.
+ *
+ ******************************************************************************
+ */
+
+static int
+mainRun(int argc,
+ char *argv[])
+{
+ VGAuthError err;
+ VGAuthContext *ctx = NULL;
+ gboolean doAdd = FALSE;
+ gboolean doRemove = FALSE;
+ gboolean doList = FALSE;
+ gboolean addMapped = FALSE;
+ gchar **argvCopy = NULL;
+ int argcCopy;
+ char *userName = NULL;
+ char *pemFilename = NULL;
+ gchar *comment = NULL;
+ gchar *summaryMsg;
+ gchar *subject = NULL;
+ const gchar *lUsername = SU_(cmdline.summary.username, "username");
+ const gchar *lSubject = SU_(cmdline.summary.subject, "subject");
+ const gchar *lPEMfile = SU_(cmdline.summary.pemfile, "PEM-file");
+ const gchar *lComm = SU_(cmdline.summary.comm, "comment");
+ GError *gErr = NULL;
+ PrefHandle prefs;
+ gchar *msgCatalog = NULL;
+ GOptionEntry listOptions[] = {
+ { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
+ SU_(listoptions.username,
+ "User whose certificate store is being queried"), NULL },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ SU_(listoptions.verbose,
+ "Verbose operation"), NULL },
+ { NULL }
+ };
+ GOptionEntry removeOptions[] = {
+ { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
+ SU_(removeoptions.username,
+ "User whose certificate store is being removed from"), NULL },
+ { "file", 'f', 0, G_OPTION_ARG_STRING, &pemFilename,
+ SU_(removeoptions.file, "PEM file name"), NULL },
+ { "subject", 's', 0, G_OPTION_ARG_STRING, &subject,
+ SU_(removeoptions.subject, "The SAML subject"), NULL },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ SU_(removeoptions.verbose, "Verbose operation"), NULL },
+ { NULL }
+ };
+ GOptionEntry addOptions[] = {
+ { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
+ SU_(addoptions.username,
+ "User whose certificate store is being added to"), NULL },
+ { "file", 'f', 0, G_OPTION_ARG_STRING, &pemFilename,
+ SU_(addoptions.file, "PEM file name"), NULL },
+ { "subject", 's', 0, G_OPTION_ARG_STRING, &subject,
+ SU_(addoptions.subject, "The SAML subject"), NULL },
+ { "global", 'g', 0, G_OPTION_ARG_NONE, &addMapped,
+ SU_(addoptions.global,
+ "Add the certificate to the global mapping file"), NULL },
+ { "comment", 'a', 0, G_OPTION_ARG_STRING, &comment,
+ SU_(addoptions.comment, "subject comment"), NULL},
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ SU_(addoptions.verbose, "Verbose operation"), NULL },
+ { NULL }
+ };
+ GOptionContext *context;
+
+ appName = g_basename(argv[0]);
+
+ /*
+ * The option parser needs to modify these, and using the variables
+ * coming into main doesn't work.
+ */
+ argcCopy = argc;
+ argvCopy = argv;
+
+ /*
+ * Do this first, so any noise form the locale setup is properly filtered.
+ */
+ VGAuth_SetLogHandler(CliLog, NULL, 0, NULL);
+
+ /*
+ * Find the location of the i18n catalogs.
+ */
+ prefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME);
+ msgCatalog = Pref_GetString(prefs,
+ VGAUTH_PREF_LOCALIZATION_DIR,
+ VGAUTH_PREF_GROUP_NAME_LOCALIZATION,
+ VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG);
+
+ I18n_BindTextDomain(VMW_TEXT_DOMAIN, // domain -- base name of vmsg files
+ NULL, // locale -- let it figure it out
+ msgCatalog); // path to message catalogs
+ g_free(msgCatalog);
+
+ /*
+ * Set up the option parser
+ */
+ g_set_prgname(appName);
+ context = g_option_context_new("[add | list | remove]\n");
+ summaryMsg = g_strdup_printf(
+ "add --global --username=%s --file=%s --subject=%s "
+ "[ --comment=%s ]\n"
+ "remove --username=%s --file=%s [ --subject=%s ]\n"
+ "list [ --username=%s ]\n",
+ lUsername, lPEMfile, lSubject, lComm,
+ lUsername, lPEMfile, lSubject,
+ lUsername);
+
+ g_option_context_set_summary(context, summaryMsg);
+ g_free(summaryMsg);
+ if (argc < 2) {
+ Usage(context);
+ }
+
+ /*
+ * Determine the command and set up the appropriate option table.
+ */
+ if (strcmp(argvCopy[1], "add") == 0) {
+ doAdd = TRUE;
+ g_option_context_add_main_entries(context, addOptions, NULL);
+ } else if (strcmp(argvCopy[1], "remove") == 0) {
+ doRemove = TRUE;
+ g_option_context_add_main_entries(context, removeOptions, NULL);
+ } else if (strcmp(argvCopy[1], "list") == 0) {
+ doList = TRUE;
+ g_option_context_add_main_entries(context, listOptions, NULL);
+ } else {
+ Usage(context);
+ }
+
+ /*
+ * Pull out the options.
+ */
+#ifdef _WIN32
+ {
+ char * val = getenv("CHARSET");
+ char *saved = g_strdup_printf("CHARSET=%s", val ? val : "");
+
+ /*
+ * Force the glib parser to interpret the input as the UTF-8
+ * Otherwise, glib treat the input encoding as the current code page,
+ * e.g. 1252
+ */
+ _putenv("CHARSET=UTF-8");
+#endif
+
+ if (!g_option_context_parse(context, &argcCopy, &argvCopy, &gErr)) {
+ g_printerr("%s: %s: %s\n", appName,
+ SU_(cmdline.parse, "Command line parsing failed"),
+ gErr->message);
+ g_error_free(gErr);
+ exit(-1);
+ }
+
+#ifdef _WIN32
+ _putenv("CHARSET=");
+ g_free(saved);
+ }
+#endif
+
+ /*
+ * XXX pull this if we use stdin for the cert contents.
+ */
+ if ((doAdd || doRemove) && !pemFilename) {
+ Usage(context);
+ }
+
+ err = VGAuth_Init(appName, 0, NULL, &ctx);
+ if (VGAUTH_E_OK != err) {
+ g_printerr("%s\n", SU_(vgauth.init.failed, "Failed to init VGAuth"));
+ exit(-1);
+ }
+
+ /*
+ * XXX
+ * If username is unset, should it use the current user?
+ * This breaks the model where no username means listMapped.
+ * Can we do it just for add/remove, or is that too confusing?
+ * Add an explicit listmapped instead?
+ */
+
+ if (doAdd) {
+ err = CliAddAlias(ctx, userName, subject, pemFilename, addMapped, comment);
+ } else if (doRemove) {
+ err= CliRemoveAlias(ctx, userName, subject, pemFilename);
+ } else if (doList) {
+ if (userName) {
+ err = CliList(ctx, userName);
+ } else {
+ err = CliListMapped(ctx);
+ }
+ }
+
+ VGAuth_Shutdown(ctx);
+ return (err == VGAUTH_E_OK) ? 0 : -1;
+}
+
+
+#ifdef _WIN32
+
+
+/*
+ ******************************************************************************
+ * wmain -- */ /**
+ *
+ * Initializes and parses commandline args.
+ *
+ * @param[in] argc Number of command line arguments.
+ * @param[in] argv The command line arguments in unicode.
+ *
+ * @return 0 if the operation ran successfully, -1 if there was an error during
+ * execution.
+ *
+ ******************************************************************************
+ */
+
+int
+wmain(int argc,
+ wchar_t *argv[])
+{
+ int retval = -1;
+ int i;
+ char **argvUtf8 = g_malloc0((argc + 1) * sizeof (char*));
+
+ for (i = 0; i < argc; ++i) {
+ CHK_UTF16_TO_UTF8(argvUtf8[i], argv[i], goto end);
+ }
+
+ retval = mainRun(argc, argvUtf8);
+
+end:
+
+ for (i = 0; i < argc; ++i) {
+ g_free(argvUtf8[i]);
+ }
+
+ g_free(argvUtf8);
+
+ return retval;
+}
+
+#else
+
+
+/*
+ ******************************************************************************
+ * main -- */ /**
+ *
+ * Initializes and parses commandline args.
+ *
+ * @param[in] argc Number of command line arguments.
+ * @param[in] argv The command line arguments.
+ *
+ * @return 0 if the operation ran successfully, -1 if there was an error during
+ * execution.
+ *
+ ******************************************************************************
+ */
+
+int
+main(int argc,
+ char *argv[])
+{
+ return mainRun(argc, argv);
+}
+
+#endif