summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2014-01-04 15:15:39 -0500
committerSimo Sorce <simo@redhat.com>2014-01-06 21:27:55 -0500
commitd5f34554229b2e41dc4147547686a2c3f494d745 (patch)
treecc787351aae66ba7c79740eadb342533ee835703
parent8a1f41674541eb0d1c5df9c090cec1674f2d9284 (diff)
downloadgss-proxy-d5f34554229b2e41dc4147547686a2c3f494d745.tar.gz
gss-proxy-d5f34554229b2e41dc4147547686a2c3f494d745.tar.xz
gss-proxy-d5f34554229b2e41dc4147547686a2c3f494d745.zip
Add facility to manage encrypted user credentials
Signed-off-by: Simo Sorce <simo@redhat.com>
-rw-r--r--proxy/Makefile.am22
-rw-r--r--proxy/external/libkeyutils.m42
-rw-r--r--proxy/src/gp_common.h17
-rw-r--r--proxy/src/gp_secrets.c343
-rw-r--r--proxy/src/gp_secrets.h34
-rw-r--r--proxy/src/gpcc.c226
6 files changed, 637 insertions, 7 deletions
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index 10f4e30..a6c64e3 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -29,6 +29,7 @@ pkgconfigdir = $(libdir)/pkgconfig
gpstatedir = @gpstatedir@
gpclidir = @gpstatedir@/clients
+gpprivdir = @gpstatedir@/private
AM_CFLAGS =
if WANT_AUX_INFO
@@ -48,6 +49,9 @@ ACLOCAL_AMFLAGS = -I m4 -I .
sbin_PROGRAMS = \
gssproxy
+bin_PROGRAMS = \
+ gpcc
+
check_PROGRAMS = \
cli_srv_comm interposetest
@@ -81,7 +85,9 @@ AM_CPPFLAGS = \
EXTRA_DIST = build/config.rpath
-GSS_PROXY_LIBS = $(POPT_LIBS) $(KRB5_LIBS) $(VERTO_LIBS) $(INI_LIBS) $(GSSAPI_LIBS) $(GSSRPC_LIBS)
+GSS_PROXY_LIBS = $(POPT_LIBS) $(VERTO_LIBS) $(INI_LIBS) \
+ $(KRB5_LIBS) $(GSSAPI_LIBS) $(GSSRPC_LIBS) \
+ $(KEYUTILS_LIBS)
if BUILD_SELINUX
GSS_PROXY_LIBS += $(SELINUX_LIBS)
@@ -137,6 +143,7 @@ dist_noinst_HEADERS = \
src/gp_rpc_creds.h \
src/gp_selinux.h \
src/gp_crypto.h \
+ src/gp_secrets.h \
src/mechglue/gss_plugin.h
@@ -160,6 +167,7 @@ gssproxy_SOURCES = \
src/gp_log.c \
src/gp_util.c \
src/gp_crypto.c \
+ src/gp_secrets.c \
src/gp_rpc_accept_sec_context.c \
src/gp_rpc_release_handle.c \
src/gp_rpc_acquire_cred.c \
@@ -196,6 +204,14 @@ interposetest_SOURCES = \
src/gp_debug.c \
tests/interposetest.c
+gpcc_SOURCES = \
+ src/gp_debug.c \
+ src/gp_log.c \
+ src/gp_util.c \
+ src/gp_crypto.c \
+ src/gp_secrets.c \
+ src/gpcc.c
+
gssproxy_LDADD = \
$(GSS_PROXY_LIBS)
@@ -205,6 +221,9 @@ cli_srv_comm_LDADD = \
interposetest_LDADD = \
$(GSS_PROXY_LIBS)
+gpcc_LDADD = \
+ $(GSS_PROXY_LIBS)
+
dist_noinst_DATA += \
m4
@@ -241,6 +260,7 @@ installgsspdirs::
$(DESTDIR)$(logpath) \
$(DESTDIR)$(gpstatedir) \
$(DESTDIR)$(gpclidir) \
+ $(DESTDIR)$(gpprivdir) \
$(DESTDIR)$(pubconfpath)
if HAVE_DOXYGEN
diff --git a/proxy/external/libkeyutils.m4 b/proxy/external/libkeyutils.m4
index 5753d77..1dee80b 100644
--- a/proxy/external/libkeyutils.m4
+++ b/proxy/external/libkeyutils.m4
@@ -1,7 +1,7 @@
AC_SUBST(KEYUTILS_LIBS)
AC_CHECK_HEADERS([keyutils.h],
- [AC_CHECK_LIB([keyutils], [add_key],
+ [AC_CHECK_LIB([keyutils], [keyctl_get_persistent],
[AC_DEFINE(USE_KEYRING, 1, [Define if the keyring should be used])
KEYUTILS_LIBS="-lkeyutils"
],
diff --git a/proxy/src/gp_common.h b/proxy/src/gp_common.h
index 7b3e9ac..ceca481 100644
--- a/proxy/src/gp_common.h
+++ b/proxy/src/gp_common.h
@@ -57,11 +57,6 @@
elem->next = NULL; \
} while (0)
-#define safefree(ptr) do { \
- free(no_const(ptr)); \
- ptr = NULL; \
-} while(0)
-
/* max out at 1MB for now */
#define MAX_RPC_SIZE 1024*1024
@@ -73,6 +68,18 @@ ssize_t gp_safe_read(int fd, void *buf, size_t count);
ssize_t gp_safe_write(int fd, const void *buf, size_t count);
void gp_safe_zero(void *buf, size_t len);
+#define safefree(ptr) \
+if (ptr) { \
+ free(no_const(ptr)); \
+ ptr = NULL; \
+}
+
+#define strzerofree(str) \
+if (str) { \
+ gp_safe_zero((str), strlen(str)); \
+ safefree(str); \
+}
+
/* NOTE: read the note in gp_util.c before using gp_strerror() */
char *gp_strerror(int errnum);
diff --git a/proxy/src/gp_secrets.c b/proxy/src/gp_secrets.c
new file mode 100644
index 0000000..af63b3a
--- /dev/null
+++ b/proxy/src/gp_secrets.c
@@ -0,0 +1,343 @@
+/*
+ GSS-PROXY
+
+ Copyright (C) 2013 Simo Sorce <simo.sorce@redhat.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <keyutils.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include "gp_common.h"
+#include "gp_crypto.h"
+#include "gp_secrets.h"
+
+#define PRIV_PATH GPSTATE_PATH"/private"
+
+#define USER_KEY "user"
+#define CREDS_KEY_NAME "GSS-Proxy-Creds-Key"
+
+int gp_priv_init(void)
+{
+ struct gp_crypto_key *encryption_key;
+ key_serial_t keyring, keyid;
+ size_t len;
+ void *buf;
+ int ret;
+
+ /* create private dir if necessary */
+ ret = mkdir(PRIV_PATH, S_IRWXU);
+ if (ret == -1 && errno != EEXIST) {
+ ret = errno;
+ GPERROR("Failed to create %s directory.\n", PRIV_PATH);
+ return ret;
+ }
+
+ keyring = keyctl_get_persistent(getuid(), KEY_SPEC_PROCESS_KEYRING);
+ if (keyring == -1) {
+ ret = errno;
+ GPERROR("Failed to get persistent keyring: (%d) %s\n",
+ ret, gp_strerror(ret));
+ return ret;
+ }
+
+ keyid = keyctl_search(keyring, USER_KEY, CREDS_KEY_NAME, 0);
+ if (keyid == -1) {
+ /* not found, generate a new key */
+ encryption_key = gp_crypto_new_key();
+ if (!encryption_key) {
+ GPERROR("Failed to generate key\n");
+ return EINVAL;
+ }
+ ret = gp_crypto_export_key(encryption_key, &buf, &len);
+ if (ret) {
+ GPERROR("Failed to serialize key\n");
+ return ret;
+ }
+ keyid = add_key(USER_KEY, CREDS_KEY_NAME, buf, len, keyring);
+ if (keyid == -1) {
+ ret = errno;
+ GPERROR("Failed to store random key: (%d) %s\n",
+ ret, gp_strerror(ret));
+ return ret;
+ }
+
+ gp_safe_zero(buf, len);
+ }
+
+ return 0;
+}
+
+static int get_crypto_key(struct gp_crypto_key **key)
+{
+ key_serial_t keyring, keyid;
+ void *kbuf = NULL;
+ size_t klen = 0;
+ int ret;
+
+ keyring = keyctl_get_persistent(getuid(), KEY_SPEC_PROCESS_KEYRING);
+ if (keyring == -1) {
+ ret = errno;
+ GPERROR("Failed to get persistent keyring: (%d) %s\n",
+ ret, gp_strerror(ret));
+ return ret;
+ }
+
+ keyid = keyctl_search(keyring, USER_KEY, CREDS_KEY_NAME, 0);
+ if (keyid == -1) {
+ ret = errno;
+ GPERROR("Failed to get encryption key: (%d) %s\n",
+ ret, gp_strerror(ret));
+ return ret;
+ }
+
+ klen = keyctl_read_alloc(keyid, &kbuf);
+ if (klen == -1) {
+ ret = errno;
+ GPERROR("Failed to read encryption key: (%d) %s\n",
+ ret, gp_strerror(ret));
+ return ret;
+ }
+
+ ret = gp_crypto_import_key(kbuf, klen, key);
+ if (ret) {
+ GPERROR("Failed to serialize key: (%d) %s\n",
+ ret, gp_strerror(ret));
+ }
+
+ gp_safe_zero(kbuf, klen);
+ free(kbuf);
+
+ return ret;
+}
+
+static int decrypt_user_creds(char *buf, size_t len)
+{
+ struct gp_crypto_key *encryption_key;
+ void *tmpbuf;
+ size_t tmplen;
+ int ret;
+
+ ret = get_crypto_key(&encryption_key);
+ if (ret) {
+ return ret;
+ }
+
+ ret = gp_crypto_decrypt(encryption_key, buf, len, &tmpbuf, &tmplen);
+ if (ret) {
+ GPERROR("Failed to decrypt user creds: (%d) %s\n",
+ ret, gp_strerror(ret));
+ goto done;
+ }
+
+ /* copy plaintext over */
+ memcpy(buf, tmpbuf, tmplen);
+ buf[tmplen] = '\0';
+
+ /* then zero and free temporary buffer */
+ gp_safe_zero(tmpbuf, tmplen);
+ free(tmpbuf);
+
+done:
+ gp_crypto_free_key(&encryption_key);
+ return ret;
+}
+
+static int encrypt_user_creds(void *in, size_t inlen,
+ void **out, size_t *outlen)
+{
+ struct gp_crypto_key *encryption_key;
+ int ret;
+
+ ret = get_crypto_key(&encryption_key);
+ if (ret) {
+ return ret;
+ }
+
+ ret = gp_crypto_encrypt(encryption_key, in, inlen, out, outlen);
+ if (ret) {
+ GPERROR("Failed to encrypt user creds: (%d) %s\n",
+ ret, gp_strerror(ret));
+ }
+
+ gp_crypto_free_key(&encryption_key);
+ return ret;
+}
+
+#define MAX_CREDS_SIZE 1024
+
+int gp_get_uid_creds(uid_t uid, char **domain,
+ char **username, char **password)
+{
+ char *filename;
+ char *s, *e;
+ char buf[MAX_CREDS_SIZE + 1];
+ int len;
+ int ret;
+ int fd;
+
+ ret = asprintf(&filename, "%s/creds_%d", PRIV_PATH, uid);
+ if (ret == -1) {
+ return ENOMEM;
+ }
+
+ *domain = NULL;
+ *username = NULL;
+ *password = NULL;
+
+ fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ ret = errno;
+ GPERROR("Failed to open creds file [%s]: (%d) %s\n",
+ filename, ret, gp_strerror(ret));
+ goto done;
+ }
+
+ len = gp_safe_read(fd, buf, MAX_CREDS_SIZE + 1);
+ if (len == -1) {
+ ret = errno;
+ GPERROR("Failed to read creds file [%s]: (%d) %s\n",
+ filename, ret, gp_strerror(ret));
+ goto done;
+ }
+ if (len > MAX_CREDS_SIZE) {
+ GPERROR("Creds file %s is too big.\n", filename);
+ ret = E2BIG;
+ goto done;
+ }
+
+ ret = decrypt_user_creds(buf, len);
+ if (ret) goto done;
+
+ /* get domain */
+ s = buf;
+ e = strchr(s, ':');
+ if (!e) {
+ GPERROR("Invalid creds (malformed)\n");
+ ret = EINVAL;
+ goto done;
+ }
+ ret = asprintf(domain, "%.*s", (int)(e - s), s);
+ if (ret == -1) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* get username */
+ s = e + 1;
+ e = strchr(s, ':');
+ if (!e) {
+ GPERROR("Invalid creds (malformed)\n");
+ ret = EINVAL;
+ goto done;
+ }
+ ret = asprintf(username, "%.*s", (int)(e - s), s);
+ if (ret == -1) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* get password */
+ s = e + 1;
+ *password = strdup(s);
+ if (*password == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret) {
+ strzerofree(*domain);
+ strzerofree(*username);
+ strzerofree(*password);
+ }
+ gp_safe_zero(buf, MAX_CREDS_SIZE);
+ free(filename);
+ close(fd);
+ return ret;
+}
+
+int gp_save_uid_creds(uid_t uid, char *domain,
+ char *username, char *password)
+{
+ char *filename;
+ char buf[MAX_CREDS_SIZE + 1];
+ void *ebuf = NULL;
+ size_t elen = 0;
+ int len;
+ int ret;
+ int fd;
+
+ ret = asprintf(&filename, "%s/creds_%d", PRIV_PATH, uid);
+ if (ret == -1) {
+ return ENOMEM;
+ }
+
+ fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0600);
+ if (fd == -1) {
+ ret = errno;
+ GPERROR("Failed to open creds file [%s]: (%d) %s\n",
+ filename, ret, gp_strerror(ret));
+ goto done;
+ }
+
+ len = snprintf(buf, MAX_CREDS_SIZE, "%s:%s:%s",
+ domain ? domain : "", username, password);
+ if (len < 0 || len > MAX_CREDS_SIZE) {
+ GPERROR("Failed to properly format creds buffer.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = encrypt_user_creds(buf, len, &ebuf, &elen);
+ if (ret) goto done;
+
+ if (elen > MAX_CREDS_SIZE) {
+ GPERROR("Final creds buffer too long. Max is %d, got %lu.\n",
+ MAX_CREDS_SIZE, elen);
+ ret = EINVAL;
+ goto done;
+ }
+
+ len = gp_safe_write(fd, ebuf, elen);
+ if (len != elen) {
+ ret = errno;
+ GPERROR("Failed to write creds file [%s]: (%d) %s\n",
+ filename, ret, gp_strerror(ret));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ gp_safe_zero(buf, MAX_CREDS_SIZE);
+ gp_safe_zero(ebuf, elen);
+ free(filename);
+ free(ebuf);
+ close(fd);
+ return ret;
+}
diff --git a/proxy/src/gp_secrets.h b/proxy/src/gp_secrets.h
new file mode 100644
index 0000000..1c384e2
--- /dev/null
+++ b/proxy/src/gp_secrets.h
@@ -0,0 +1,34 @@
+/*
+ GSS-PROXY
+
+ Copyright (C) 2013 Simo Sorce <simo.sorce@redhat.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef _GP_SECRETS_H_
+#define _GP_SECRETS_H_
+
+int gp_priv_init(void);
+int gp_get_uid_creds(uid_t uid, char **domain,
+ char **username, char **password);
+int gp_save_uid_creds(uid_t uid, char *domain,
+ char *username, char *password);
+
+#endif /* _GP_SECRETS_H_ */
diff --git a/proxy/src/gpcc.c b/proxy/src/gpcc.c
new file mode 100644
index 0000000..44a2690
--- /dev/null
+++ b/proxy/src/gpcc.c
@@ -0,0 +1,226 @@
+/*
+ GSS-PROXY
+
+ Copyright (C) 2011 Red Hat, Inc.
+ Copyright (C) 2011 Simo Sorce <simo.sorce@redhat.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#include "config.h"
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <termios.h>
+#include <errno.h>
+#include "popt.h"
+#include "gp_common.h"
+#include "gp_secrets.h"
+
+#define _(STRING) gettext(STRING)
+
+char *fgets_password(void)
+{
+ struct termios old_flags, flags;
+ char buf[1024];
+ char *pwd;
+ int ret;
+
+ ret = tcgetattr(fileno(stdin), &old_flags);
+ flags = old_flags;
+ flags.c_lflag = (flags.c_lflag & ~ECHO) | ECHONL;
+
+ ret = tcsetattr(fileno(stdin), TCSANOW, &flags);
+ if (ret) return NULL;
+
+ fprintf(stdout, "Password: ");
+ pwd = fgets(buf, 1024, stdin);
+
+ /* nothing we can do if this fails anyway */
+ (void)tcsetattr(fileno(stdin), TCSANOW, &old_flags);
+
+ if (pwd) {
+ buf[strlen(buf) - 1] = '\0';
+ pwd = strdup(buf);
+ }
+
+ gp_safe_zero(buf, 1024);
+ return pwd;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ const char *pc_arg;
+ const char *opt_type = "NTLMSSP";
+ uid_t realuid = getuid();
+ long opt_uid = realuid;
+ int opt_version = 0;
+ int opt_debug = 0;
+ char *domain = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ const char *p;
+ int ret;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "debug", 'd', POPT_ARG_NONE | POPT_ARGFLAG_SHOW_DEFAULT,
+ &opt_debug, 0, _("Enable debugging"), NULL },
+ { "version", '\0', POPT_ARG_NONE,
+ &opt_version, 0, _("Print version number and exit"), NULL },
+ { "type", 't', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
+ &opt_type, 0, _("Type of credentials"), NULL },
+ { "uid", 'u', POPT_ARG_LONG | POPT_ARGFLAG_SHOW_DEFAULT,
+ &opt_uid, 0, _("UserID owning of the credentials"), NULL },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ poptSetOtherOptionHelp(pc, "[[domain:]username]");
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+ }
+ pc_arg = poptGetArg(pc);
+ if (opt_version != 0 && pc_arg != NULL) {
+ fprintf(stderr, "\nThe version option does not take an argument\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ if (opt_version) {
+ puts(VERSION""DISTRO_VERSION""PRERELEASE_VERSION);
+ ret = 0;
+ goto done;
+ }
+
+ if (opt_debug) {
+ gp_debug_enable();
+ }
+
+ if (strcasecmp(opt_type, "NTLMSSP") != 0) {
+ fprintf(stderr, "\nUnsupported type %s\n", opt_type);
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ /* FIXME: if gssproxy is configure with run_as_user, we'll not use the
+ * right keyring here. We need to drop privileges like gssproxy does, which
+ * means reading the gssproxy.conf file. */
+ if (geteuid() != 0) {
+ if (opt_debug && opt_uid == 0) {
+ fprintf(stderr, "###### This is a test mode ######\n");
+ fprintf(stderr, "Please do not use real credentials\n");
+ realuid = 0;
+ } else {
+ fprintf(stderr, "This program requires root privileges\n");
+ return -EPERM;
+ }
+ }
+
+ /* only root can specify an arbitrary uid */
+ if (realuid != 0) {
+ opt_uid = realuid;
+ }
+
+ ret = gp_priv_init();
+ if (ret) {
+ fprintf(stderr, "Failed to initialize security infrastructure\n");
+ fprintf(stderr, "Error: (%d) %s\n", ret, gp_strerror(ret));
+ return -1;
+ }
+
+ if (pc_arg == NULL && realuid != 0) {
+ /* only root can read the credentials back */
+ fprintf(stderr, "\nPlease provide a [domain:]username option\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ if (pc_arg == NULL) {
+ ret = gp_get_uid_creds(opt_uid, &domain, &username, &password);
+ if (ret) {
+ fprintf(stderr, "\nCredentials not found: (%d) %s\n",
+ ret, gp_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+ fprintf(stderr, "\nFound credentials for uid %ld\n", opt_uid);
+ fprintf(stderr, "Username: %s\n", username);
+ fprintf(stderr, "Domain: %s\n", domain);
+
+ ret = 0;
+ goto done;
+ }
+
+ p = strchr(pc_arg, ':');
+ if (p) {
+ domain = strndup(pc_arg, p - pc_arg);
+ if (!domain) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ p += 1;
+ } else {
+ p = pc_arg;
+ }
+ username = strdup(p);
+ if (!username) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ password = fgets_password();
+ if (!password) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = gp_save_uid_creds(opt_uid, domain, username, password);
+ if (ret) {
+ fprintf(stderr, "\nFailed to save credentials for uid %ld\n", opt_uid);
+ ret = 1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ poptFreeContext(pc);
+ strzerofree(domain);
+ strzerofree(username);
+ strzerofree(password);
+ return ret;
+}