@@ -40,6 +40,7 @@
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include <keyutils.h>
#include <time.h>
@@ -154,12 +155,127 @@ err_cache:
return credtime;
}
+#define CIFS_UPCALL_ENV_PATH_FMT "/proc/%d/environ"
+#define CIFS_UPCALL_ENV_PATH_MAXLEN (6 + 10 + 8 + 1)
+
+#define ENV_NAME "KRB5CCNAME"
+#define ENV_PREFIX "KRB5CCNAME="
+#define ENV_PREFIX_LEN 11
+
+#define ENV_BUF_START (4096)
+#define ENV_BUF_MAX (131072)
+
+/**
+ * get_cachename_from_process_env - scrape value of $KRB5CCNAME out of the
+ * initiating process' environment.
+ * @pid: initiating pid value from the upcall string
+ *
+ * Open the /proc/<pid>/environ file for the given pid, and scrape it for
+ * KRB5CCNAME entries.
+ *
+ * We start with a page-size buffer, and then progressively double it until
+ * we can slurp in the whole thing.
+ *
+ * Note that this is not entirely reliable. If the process is sitting in a
+ * container or something, then this is almost certainly not going to point
+ * where you expect.
+ *
+ * Probably it just won't work, but could a user use this to trick cifs.upcall
+ * into reading a file outside the container, by setting KRB5CCNAME in a
+ * crafty way?
+ *
+ * YMMV here.
+ */
+static char *
+get_cachename_from_process_env(pid_t pid)
+{
+ int fd, ret;
+ ssize_t buflen;
+ ssize_t bufsize = ENV_BUF_START;
+ char pathname[CIFS_UPCALL_ENV_PATH_MAXLEN];
+ char *cachename = NULL;
+ char *buf = NULL, *pos;
+
+ if (!pid) {
+ syslog(LOG_DEBUG, "%s: pid == 0\n", __func__);
+ return NULL;
+ }
+
+ pathname[CIFS_UPCALL_ENV_PATH_MAXLEN - 1] = '\0';
+ ret = snprintf(pathname, CIFS_UPCALL_ENV_PATH_MAXLEN,
+ CIFS_UPCALL_ENV_PATH_FMT, pid);
+ if (ret || pathname[CIFS_UPCALL_ENV_PATH_MAXLEN - 1] != '\0') {
+ syslog(LOG_DEBUG, "%s: unterminated path!\n", __func__);
+ return NULL;
+ }
+
+ syslog(LOG_DEBUG, "%s: pathname=%s\n", __func__, pathname);
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_DEBUG, "%s: open failed: %d\n", __func__, errno);
+ return NULL;
+ }
+retry:
+ if (bufsize > ENV_BUF_MAX) {
+ syslog(LOG_DEBUG, "%s: buffer too big: %d\n", __func__, errno);
+ goto out_close;
+ }
+
+ buf = malloc(bufsize);
+ if (!buf) {
+ syslog(LOG_DEBUG, "%s: malloc failure\n", __func__);
+ goto out_close;
+ }
+
+ buflen = read(fd, buf, bufsize);
+ if (buflen < 0) {
+ syslog(LOG_DEBUG, "%s: read failed: %d\n", __func__, errno);
+ goto out_close;
+ }
+
+ if (buflen == bufsize) {
+ /* We read to the end of the buffer, double and try again */
+ syslog(LOG_DEBUG, "%s: read to end of buffer (%zu bytes)\n", __func__, bufsize);
+ free(buf);
+ bufsize *= 2;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out_close;
+ goto retry;
+ }
+
+ pos = buf;
+ while (buflen > 0) {
+ size_t len = strnlen(pos, buflen);
+
+ if (len > ENV_PREFIX_LEN &&
+ memcmp(pos, ENV_PREFIX, ENV_PREFIX_LEN)) {
+ cachename = strndup(pos + ENV_PREFIX_LEN,
+ len - ENV_PREFIX_LEN);
+ syslog(LOG_DEBUG, "%s: cachename = %s\n", __func__, cachename);
+ break;
+ }
+ buflen -= (len + 1);
+ pos += (len + 1);
+ }
+ free(buf);
+out_close:
+ close(fd);
+ return cachename;
+}
+
static krb5_ccache
-get_default_cc(void)
+get_existing_cc(pid_t pid)
{
krb5_error_code ret;
krb5_ccache cc;
- char *cachename;
+ char *cachename = NULL;
+
+ cachename = get_cachename_from_process_env(pid);
+ if (cachename) {
+ if (setenv(ENV_NAME, cachename, 1))
+ syslog(LOG_DEBUG, "%s: failed to setenv %d\n", __func__, errno);
+ free(cachename);
+ }
ret = krb5_cc_default(context, &cc);
if (ret) {
@@ -182,7 +298,6 @@ get_default_cc(void)
return cc;
}
-
static krb5_ccache
init_cc_from_keytab(const char *keytab_name, const char *user)
{
@@ -815,7 +930,7 @@ int main(const int argc, char *const argv[])
goto out;
}
- ccache = get_default_cc();
+ ccache = get_existing_cc(arg.pid);
/* Couldn't find credcache? Try to use keytab */
if (ccache == NULL && arg.username != NULL)
ccache = init_cc_from_keytab(keytab_name, arg.username);