예제 #1
0
/*
 * Find the name@domain string from either a user or group id
 */
int name_lookup(char *id, key_serial_t key, int type)
{
	char name[IDMAP_NAMESZ];
	char domain[NFS4_MAX_DOMAIN_LEN];
	uid_t uid;
	gid_t gid;
	int rc;

	rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN);
	if (rc != 0) {
		xlog_errno(rc,
			"name_lookup: nfs4_get_default_domain failed: %m");
		rc = -1;
		goto out;
	}

	if (type == USER) {
		uid = atoi(id);
		rc = nfs4_uid_to_name(uid, domain, name, IDMAP_NAMESZ);
	} else {
		gid = atoi(id);
		rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ);
	}
	if (rc < 0)
		xlog_errno(rc, "name_lookup: %s: failed: %m",
			(type == USER ? "nfs4_uid_to_name" : "nfs4_gid_to_name"));

	if (rc == 0) {
		rc = keyctl_instantiate(key, &name, strlen(name), 0);
		if (rc < 0)
			xlog_err("name_lookup: keyctl_instantiate failed: %m");
	}
out:
	return rc;
}
예제 #2
0
/*
 * Find either a user or group id based on the name@domain string
 */
static int id_lookup(char *name_at_domain, key_serial_t key, int type)
{
	char id[MAX_ID_LEN];
	uid_t uid = 0;
	gid_t gid = 0;
	int rc;

	if (type == USER) {
		rc = nfs4_owner_to_uid(name_at_domain, &uid);
		sprintf(id, "%u", uid);
	} else {
		rc = nfs4_group_owner_to_gid(name_at_domain, &gid);
		sprintf(id, "%u", gid);
	}
	if (rc < 0) {
		xlog_errno(rc, "id_lookup: %s: failed: %m",
			(type == USER ? "nfs4_owner_to_uid" : "nfs4_group_owner_to_gid"));
		return EXIT_FAILURE;
	}

	rc = EXIT_SUCCESS;
	if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) {
		switch (errno) {
		case EDQUOT:
		case ENFILE:
		case ENOMEM:
			/*
			 * The keyring is full. Clear the keyring and try again
			 */
			rc = keyring_clear(DEFAULT_KEYRING);
			if (rc)
				break;
			if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) {
				rc = EXIT_FAILURE;
				xlog_err("id_lookup: keyctl_instantiate failed: %m");
			}
			break;
		default:
			rc = EXIT_FAILURE;
			break;
		}
	}

	return rc;
}
예제 #3
0
static int cifs_resolver(const key_serial_t key, const char *key_descr)
{
	int c;
	struct addrinfo *addr;
	char ip[INET6_ADDRSTRLEN];
	void *p;
	const char *keyend = key_descr;
	/* skip next 4 ';' delimiters to get to description */
	for (c = 1; c <= 4; c++) {
		keyend = index(keyend + 1, ';');
		if (!keyend) {
			syslog(LOG_ERR, "invalid key description: %s",
			       key_descr);
			return 1;
		}
	}
	keyend++;

	/* resolve name to ip */
	c = getaddrinfo(keyend, NULL, NULL, &addr);
	if (c) {
		syslog(LOG_ERR, "unable to resolve hostname: %s [%s]",
		       keyend, gai_strerror(c));
		return 1;
	}

	/* conver ip to string form */
	if (addr->ai_family == AF_INET)
		p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
	else
		p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);

	if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
		syslog(LOG_ERR, "%s: inet_ntop: %s", __func__, strerror(errno));
		freeaddrinfo(addr);
		return 1;
	}

	/* setup key */
	c = keyctl_instantiate(key, ip, strlen(ip) + 1, 0);
	if (c == -1) {
		syslog(LOG_ERR, "%s: keyctl_instantiate: %s", __func__,
		       strerror(errno));
		freeaddrinfo(addr);
		return 1;
	}

	freeaddrinfo(addr);
	return 0;
}
예제 #4
0
static int
cifs_idmap(const key_serial_t key, const char *key_descr)
{
	int rc = 1;
	char *sidstr = NULL;
	struct cifs_sid sid;
	struct cifs_uxid cuxid;

	/*
	 * Use winbind to convert received string to a SID and lookup
	 * name and map that SID to an uid.  If either of these
	 * function calls return with an error, return an error the
	 * upcall caller.  Otherwise instanticate a key using that uid.
	 *
	 * The same applies to SID and gid mapping.
	 */
	sidstr = strget(key_descr, "os:");
	if (sidstr) {
		rc = str_to_sid(plugin_handle, sidstr, &sid);
		if (rc) {
			syslog(LOG_DEBUG, "Unable to convert owner string %s "
				"to SID: %s", key_descr, plugin_errmsg);
			goto cifs_idmap_ret;
		}

		rc = sids_to_ids(plugin_handle, &sid, 1, &cuxid);
		if (rc || (cuxid.type != CIFS_UXID_TYPE_UID &&
			   cuxid.type != CIFS_UXID_TYPE_BOTH)) {
			syslog(LOG_DEBUG, "Unable to convert %s to "
				"UID: %s", key_descr, plugin_errmsg);
			rc = rc ? rc : -EINVAL;
			goto cifs_idmap_ret;
		}
		rc = keyctl_instantiate(key, &cuxid.id.uid, sizeof(uid_t), 0);
		if (rc)
			syslog(LOG_ERR, "%s: key inst: %s", __func__,
					strerror(errno));

		goto cifs_idmap_ret;
	}

	sidstr = strget(key_descr, "gs:");
	if (sidstr) {
		rc = str_to_sid(plugin_handle, sidstr, &sid);
		if (rc) {
			syslog(LOG_DEBUG, "Unable to convert group string %s "
				"to SID: %s", key_descr, plugin_errmsg);
			goto cifs_idmap_ret;
		}

		rc = sids_to_ids(plugin_handle, &sid, 1, &cuxid);
		if (rc || (cuxid.type != CIFS_UXID_TYPE_GID &&
			   cuxid.type != CIFS_UXID_TYPE_BOTH)) {
			syslog(LOG_DEBUG, "Unable to convert %s to "
				"GID: %s", key_descr, plugin_errmsg);
			rc = rc ? rc : -EINVAL;
			goto cifs_idmap_ret;
		}
		rc = keyctl_instantiate(key, &cuxid.id.gid, sizeof(gid_t), 0);
		if (rc)
			syslog(LOG_ERR, "%s: key inst: %s", __func__,
					strerror(errno));

		goto cifs_idmap_ret;
	}

	sidstr = strget(key_descr, "oi:");
	if (sidstr) {
		rc = str_to_uint(sidstr, (unsigned int *)&cuxid.id.uid);
		if (rc) {
			syslog(LOG_ERR, "Unable to convert %s to uid: %s",
				sidstr, strerror(rc));
			goto cifs_idmap_ret;
		}
		cuxid.type = CIFS_UXID_TYPE_UID;

		syslog(LOG_DEBUG, "SID: %s, uid: %u", sidstr, cuxid.id.uid);
		rc = ids_to_sids(plugin_handle, &cuxid, 1, &sid);
		if (rc || sid.revision == 0) {
			syslog(LOG_DEBUG, "uid %u to SID error: %s",
				cuxid.id.uid, plugin_errmsg);
			goto cifs_idmap_ret;
		}

		rc = keyctl_instantiate(key, &sid, sizeof(struct cifs_sid), 0);
		if (rc)
			syslog(LOG_ERR, "%s: key inst: %s", __func__,
				strerror(errno));

		goto cifs_idmap_ret;
	}

	sidstr = strget(key_descr, "gi:");
	if (sidstr) {
		rc = str_to_uint(sidstr, (unsigned int *)&cuxid.id.gid);
		if (rc) {
			syslog(LOG_ERR, "Unable to convert %s to gid: %s",
				sidstr, strerror(rc));
			goto cifs_idmap_ret;
		}
		cuxid.type = CIFS_UXID_TYPE_GID;

		syslog(LOG_DEBUG, "SID: %s, gid: %u", sidstr, cuxid.id.gid);
		rc = ids_to_sids(plugin_handle, &cuxid, 1, &sid);
		if (rc || sid.revision == 0) {
			syslog(LOG_DEBUG, "gid %u to SID error: %s",
				cuxid.id.gid, plugin_errmsg);
			goto cifs_idmap_ret;
		}

		rc = keyctl_instantiate(key, &sid, sizeof(struct cifs_sid), 0);
		if (rc)
			syslog(LOG_ERR, "%s: key inst: %s", __func__,
				strerror(errno));

		goto cifs_idmap_ret;
	}

	syslog(LOG_DEBUG, "Invalid key: %s", key_descr);

cifs_idmap_ret:
	return rc;
}
예제 #5
0
int main(int argc, char *argv[])
{
        struct keyring_upcall_param     uparam;
        key_serial_t                    keyid;
        key_serial_t                    sring;
        key_serial_t                    inst_keyring;
        pid_t                           child;
        struct lgss_mech_type          *mech;
        struct lgss_cred               *cred;

        set_log_level();

        logmsg(LL_TRACE, "start parsing parameters\n");
        /*
         * parse & sanity check upcall parameters
         * expected to be called with:
         * [1]:  operation
         * [2]:  key ID
         * [3]:  key type
         * [4]:  key description
         * [5]:  call out info
         * [6]:  UID
         * [7]:  GID
         * [8]:  thread keyring
         * [9]:  process keyring
         * [10]: session keyring
         */
        if (argc != 10 + 1) {
                logmsg(LL_ERR, "invalid parameter number %d\n", argc);
                return 1;
        }

        logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
               argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);

        memset(&uparam, 0, sizeof(uparam));

        if (strcmp(argv[1], "create") != 0) {
                logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
                return 1;
        }

        if (sscanf(argv[2], "%d", &keyid) != 1) {
                logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
                return 1;
        }

        if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
                logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
                return 1;
        }

        if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
                logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
                return 1;
        }

        if (sscanf(argv[10], "%d", &sring) != 1) {
                logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
                return 1;
        }

        if (parse_callout_info(argv[5], &uparam)) {
                logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
                return 1;
        }

        logmsg(LL_TRACE, "parsing parameters OK\n");

        /*
         * prepare a cred
         */
        mech = lgss_name2mech(uparam.kup_mech);
        if (mech == NULL) {
                logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
                       keyid, uparam.kup_mech);
                return 1;
        }

        if (lgss_mech_initialize(mech)) {
                logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
                       keyid, mech->lmt_name);
                return 1;
        }

        cred = lgss_create_cred(mech);
        if (cred == NULL) {
                logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
                       keyid, mech->lmt_name);
                return 1;
        }

        cred->lc_uid = uparam.kup_uid;
        cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0;
        cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0;
        cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0;
        cred->lc_tgt_nid = uparam.kup_nid;
        cred->lc_tgt_svc = uparam.kup_svc;

        if (lgss_prepare_cred(cred)) {
                logmsg(LL_ERR, "key %08x: failed to prepare credentials "
                       "for user %d\n", keyid, uparam.kup_uid);
                return 1;
        }

        /* pre initialize the key. note the keyring linked to is actually of the
         * original requesting process, not _this_ upcall process. if it's for
         * root user, don't link to any keyrings because we want fully control
         * on it, and share it among all root sessions; otherswise link to
         * session keyring.
         */
        if (cred->lc_root_flags != 0)
                inst_keyring = 0;
        else
                inst_keyring = KEY_SPEC_SESSION_KEYRING;

        if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
                logmsg(LL_ERR, "instantiate key %08x: %s\n",
                       keyid, strerror(errno));
                return 1;
        }

        logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);

        /*
         * fork a child to do the real gss negotiation
         */
        child = fork();
        if (child == -1) {
                logmsg(LL_ERR, "key %08x: can't create child: %s\n",
                       keyid, strerror(errno));
                return 1;
        } else if (child == 0) {
                return lgssc_kr_negotiate(keyid, cred, &uparam);
        }

        logmsg(LL_TRACE, "forked child %d\n", child);
        return 0;
}
/*
 * pipe the callout information to the specified program and retrieve the payload data over another
 * pipe
 */
static void pipe_to_program(char *op,
			    key_serial_t key,
			    char *ktype,
			    char *kdesc,
			    char *callout_info,
			    char *prog,
			    char **argv)
{
	char errbuf[512], payload[32768 + 1], *pp, *pc, *pe;
	int ipi[2], opi[2], epi[2], childpid;
	int ifl, ofl, efl, npay, ninfo, espace, tmp;

	debug("pipe_to_program(%s -> %s)", callout_info, prog);

	if (pipe(ipi) < 0 || pipe(opi) < 0 || pipe(epi) < 0)
		error("pipe failed: %m");

	childpid = fork();
	if (childpid == -1)
		error("fork failed: %m");

	if (childpid == 0) {
		/* child process */
		if (dup2(ipi[0], 0) < 0 ||
		    dup2(opi[1], 1) < 0 ||
		    dup2(epi[1], 2) < 0)
			error("dup2 failed: %m");
		close(ipi[0]);
		close(ipi[1]);
		close(opi[0]);
		close(opi[1]);
		close(epi[0]);
		close(epi[1]);

		execv(prog, argv);
		error("/etc/request-key.conf:%d: Failed to execute '%s': %m\n", confline, prog);
	}

	/* parent process */
	close(ipi[0]);
	close(opi[1]);
	close(epi[1]);

#define TOSTDIN ipi[1]
#define FROMSTDOUT opi[0]
#define FROMSTDERR epi[0]

	ifl = fcntl(TOSTDIN, F_GETFL);
	ofl = fcntl(FROMSTDOUT, F_GETFL);
	efl = fcntl(FROMSTDERR, F_GETFL);
	if (ifl < 0 || ofl < 0 || efl < 0)
		error("fcntl/F_GETFL failed: %m");

	ifl |= O_NONBLOCK;
	ofl |= O_NONBLOCK;
	efl |= O_NONBLOCK;

	if (fcntl(TOSTDIN, F_SETFL, ifl) < 0 ||
	    fcntl(FROMSTDOUT, F_SETFL, ofl) < 0 ||
	    fcntl(FROMSTDERR, F_SETFL, efl) < 0)
		error("fcntl/F_SETFL failed: %m");

	ninfo = strlen(callout_info);
	pc = callout_info;

	npay = sizeof(payload);
	pp = payload;

	espace = sizeof(errbuf);
	pe = errbuf;

	do {
		fd_set rfds, wfds;

		FD_ZERO(&rfds);
		FD_ZERO(&wfds);

		if (TOSTDIN != -1) {
			if (ninfo > 0) {
				FD_SET(TOSTDIN, &wfds);
			}
			else {
				close(TOSTDIN);
				TOSTDIN = -1;
				continue;
			}
		}

		if (FROMSTDOUT != -1)
			FD_SET(FROMSTDOUT, &rfds);

		if (FROMSTDERR != -1)
			FD_SET(FROMSTDERR, &rfds);

		tmp = TOSTDIN > FROMSTDOUT ? TOSTDIN : FROMSTDOUT;
		tmp = tmp > FROMSTDERR ? tmp : FROMSTDERR;
		tmp++;

		debug("select r=%d,%d w=%d m=%d\n", FROMSTDOUT, FROMSTDERR, TOSTDIN, tmp);

		tmp = select(tmp, &rfds, &wfds, NULL, NULL);
		if (tmp < 0)
			error("select failed: %m\n");

		debug("select -> %d r=%x w=%x\n",
		      tmp,
		      *(unsigned *) (void *) &rfds,
		      *(unsigned *) (void *) &wfds);

		if (TOSTDIN != -1 && FD_ISSET(TOSTDIN, &wfds)) {
			tmp = write(TOSTDIN, pc, ninfo);
			if (tmp < 0) {
				if (errno != EPIPE)
					error("write failed: %m\n");

				debug("EPIPE");
				ninfo = 0;
			}
			else {
				debug("wrote %d\n", tmp);

				pc += tmp;
				ninfo -= tmp;
			}
		}

		if (FROMSTDOUT != -1 && FD_ISSET(FROMSTDOUT, &rfds)) {
			tmp = read(FROMSTDOUT, pp, npay);
			if (tmp < 0)
				error("read failed: %m\n");

			debug("read %d\n", tmp);

			if (tmp == 0) {
				close(FROMSTDOUT);
				FROMSTDOUT = -1;
			}
			else {
				pp += tmp;
				npay -= tmp;

				if (npay == 0)
					error("Too much data read from query program\n");
			}
		}

		if (FROMSTDERR != -1 && FD_ISSET(FROMSTDERR, &rfds)) {
			char *nl;

			tmp = read(FROMSTDERR, pe, espace);
			if (tmp < 0)
				error("read failed: %m\n");

			debug("read err %d\n", tmp);

			if (tmp == 0) {
				close(FROMSTDERR);
				FROMSTDERR = -1;
				continue;
			}

			pe += tmp;
			espace -= tmp;

			while ((nl = memchr(errbuf, '\n', pe - errbuf))) {
				int n, rest;

				nl++;
				n = nl - errbuf;

				if (xdebug)
					fprintf(stderr, "Child: %*.*s", n, n, errbuf);

				if (!xnolog) {
					openlog("request-key", 0, LOG_AUTHPRIV);
					syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
					closelog();
				}

				rest = pe - nl;
				if (rest > 0) {
					memmove(errbuf, nl, rest);
					pe -= n;
					espace += n;
				}
				else {
					pe = errbuf;
					espace = sizeof(errbuf);
				}
			}

			if (espace == 0) {
				int n = sizeof(errbuf);

				if (xdebug)
					fprintf(stderr, "Child: %*.*s", n, n, errbuf);

				if (!xnolog) {
					openlog("request-key", 0, LOG_AUTHPRIV);
					syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
					closelog();
				}

				pe = errbuf;
				espace = sizeof(errbuf);
			}
		}

	} while (TOSTDIN != -1 || FROMSTDOUT != -1 || FROMSTDERR != -1);

	/* wait for the program to exit */
	if (waitpid(childpid, &tmp, 0) != childpid)
		error("wait for child failed: %m\n");

	/* if the process exited non-zero or died on a signal, then we call back in to ourself to
	 * decide on negation
	 * - this is not exactly beautiful but the quickest way of having configurable negation
	 *   settings
	 */
	if (WIFEXITED(tmp) && WEXITSTATUS(tmp) != 0) {
		if (norecurse)
			error("child exited %d\n", WEXITSTATUS(tmp));

		norecurse = 1;
		debug("child exited %d\n", WEXITSTATUS(tmp));
		lookup_action("negate", key, ktype, kdesc, callout_info);
	}

	if (WIFSIGNALED(tmp)) {
		if (norecurse)
			error("child died on signal %d\n", WTERMSIG(tmp));

		norecurse = 1;
		debug("child died on signal %d\n", WTERMSIG(tmp));
		lookup_action("negate", key, ktype, kdesc, callout_info);
	}

	/* attempt to instantiate the key */
	debug("instantiate with %zd bytes\n", pp - payload);

	if (keyctl_instantiate(key, payload, pp - payload, 0) < 0)
		error("instantiate key failed: %m\n");

	debug("instantiation successful\n");
	exit(0);

} /* end pipe_to_program() */
예제 #7
0
int main(const int argc, char *const argv[])
{
	struct cifs_spnego_msg *keydata = NULL;
	DATA_BLOB secblob = data_blob_null;
	DATA_BLOB sess_key = data_blob_null;
	key_serial_t key = 0;
	size_t datalen;
	unsigned int have;
	long rc = 1;
	int c, try_dns = 0, legacy_uid = 0;
	char *buf, *ccdir = NULL, *ccname = NULL, *best_cache = NULL;
	char hostbuf[NI_MAXHOST], *host;
	struct decoded_args arg;
	const char *oid;
	uid_t uid;
	char *keytab_name = CIFS_DEFAULT_KRB5_KEYTAB;
	time_t best_time = 0;

	hostbuf[0] = '\0';
	memset(&arg, 0, sizeof(arg));

	openlog(prog, 0, LOG_DAEMON);

	while ((c = getopt_long(argc, argv, "ck:ltv", long_options, NULL)) != -1) {
		switch (c) {
		case 'c':
			/* legacy option -- skip it */
			break;
		case 't':
			try_dns++;
			break;
		case 'k':
			if (setenv("KRB5_CONFIG", optarg, 1) != 0) {
				syslog(LOG_ERR, "unable to set $KRB5_CONFIG: %d", errno);
				goto out;
			}
			break;
		case 'l':
			legacy_uid++;
			break;
		case 'v':
			printf("version: %s\n", VERSION);
			goto out;
		default:
			syslog(LOG_ERR, "unknown option: %c", c);
			goto out;
		}
	}

	/* is there a key? */
	if (argc <= optind) {
		usage();
		goto out;
	}

	/* get key and keyring values */
	errno = 0;
	key = strtol(argv[optind], NULL, 10);
	if (errno != 0) {
		key = 0;
		syslog(LOG_ERR, "Invalid key format: %s", strerror(errno));
		goto out;
	}

	rc = keyctl_describe_alloc(key, &buf);
	if (rc == -1) {
		syslog(LOG_ERR, "keyctl_describe_alloc failed: %s",
		       strerror(errno));
		rc = 1;
		goto out;
	}

	syslog(LOG_DEBUG, "key description: %s", buf);

	if ((strncmp(buf, "cifs.resolver", sizeof("cifs.resolver") - 1) == 0) ||
	    (strncmp(buf, "dns_resolver", sizeof("dns_resolver") - 1) == 0)) {
		rc = cifs_resolver(key, buf);
		goto out;
	}

	have = decode_key_description(buf, &arg);
	SAFE_FREE(buf);
	if ((have & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
		syslog(LOG_ERR, "unable to get necessary params from key "
		       "description (0x%x)", have);
		rc = 1;
		goto out;
	}

	if (arg.ver > CIFS_SPNEGO_UPCALL_VERSION) {
		syslog(LOG_ERR, "incompatible kernel upcall version: 0x%x",
		       arg.ver);
		rc = 1;
		goto out;
	}

	if (strlen(arg.hostname) >= NI_MAXHOST) {
		syslog(LOG_ERR, "hostname provided by kernel is too long");
		rc = 1;
		goto out;

	}

	if (!legacy_uid && (have & DKD_HAVE_CREDUID))
		uid = arg.creduid;
	else if (have & DKD_HAVE_UID)
		uid = arg.uid;
	else {
		/* no uid= or creduid= parm -- something is wrong */
		syslog(LOG_ERR, "No uid= or creduid= parm specified");
		rc = 1;
		goto out;
	}

	rc = setuid(uid);
	if (rc == -1) {
		syslog(LOG_ERR, "setuid: %s", strerror(errno));
		goto out;
	}
	ccdir = resolve_krb5_dir(CIFS_DEFAULT_KRB5_USER_DIR, uid);
	if (ccdir != NULL)
		find_krb5_cc(ccdir, uid, &best_cache, &best_time);
	ccname = find_krb5_cc(CIFS_DEFAULT_KRB5_DIR, uid, &best_cache,
			      &best_time);
	SAFE_FREE(ccdir);

	/* Couldn't find credcache? Try to use keytab */
	if (ccname == NULL && arg.username != NULL)
		ccname = init_cc_from_keytab(keytab_name, arg.username);

	host = arg.hostname;

	// do mech specific authorization
	switch (arg.sec) {
	case MS_KRB5:
	case KRB5:
		/*
		 * Andrew Bartlett's suggested scheme for picking a principal
		 * name, based on a supplied hostname.
		 *
		 * INPUT: fooo
		 * TRY in order:
		 * cifs/fooo@REALM
		 * cifs/fooo.<guessed domain ?>@REALM
		 *
		 * INPUT: bar.example.com
		 * TRY only:
		 * cifs/bar.example.com@REALM
		 */
		if (arg.sec == MS_KRB5)
			oid = OID_KERBEROS5_OLD;
		else
			oid = OID_KERBEROS5;

retry_new_hostname:
		lowercase_string(host);
		rc = handle_krb5_mech(oid, host, &secblob, &sess_key, ccname);
		if (!rc)
			break;

		/*
		 * If hostname has a '.', assume it's a FQDN, otherwise we
		 * want to guess the domainname.
		 */
		if (!strchr(host, '.')) {
			struct addrinfo hints;
			struct addrinfo *ai;
			char *domainname;
			char fqdn[NI_MAXHOST];

			/*
			 * use getaddrinfo() to resolve the hostname of the
			 * server and set ai_canonname.
			 */
			memset(&hints, 0, sizeof(hints));
			hints.ai_family = AF_UNSPEC;
			hints.ai_flags = AI_CANONNAME;
			rc = getaddrinfo(host, NULL, &hints, &ai);
			if (rc) {
				syslog(LOG_ERR, "Unable to resolve host address: %s [%s]",
				       host, gai_strerror(rc));
				break;
			}

			/* scan forward to first '.' in ai_canonnname */
			domainname = strchr(ai->ai_canonname, '.');
			if (!domainname) {
				rc = -EINVAL;
				freeaddrinfo(ai);
				break;
			}
			lowercase_string(domainname);
			rc = snprintf(fqdn, sizeof(fqdn), "%s%s",
					host, domainname);
			freeaddrinfo(ai);
			if (rc < 0 || (size_t)rc >= sizeof(fqdn)) {
				syslog(LOG_ERR, "Problem setting hostname in string: %ld", rc);
				rc = -EINVAL;
				break;
			}

			rc = handle_krb5_mech(oid, fqdn, &secblob, &sess_key, ccname);
			if (!rc)
				break;
		}

		if (!try_dns || !(have & DKD_HAVE_IP))
			break;

		rc = ip_to_fqdn(arg.ip, hostbuf, sizeof(hostbuf));
		if (rc)
			break;

		try_dns = 0;
		host = hostbuf;
		goto retry_new_hostname;
	default:
		syslog(LOG_ERR, "sectype: %d is not implemented", arg.sec);
		rc = 1;
		break;
	}

	if (rc) {
		syslog(LOG_DEBUG, "Unable to obtain service ticket");
		goto out;
	}

	/* pack SecurityBlob and SessionKey into downcall packet */
	datalen =
	    sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
	keydata = (struct cifs_spnego_msg *)calloc(sizeof(char), datalen);
	if (!keydata) {
		rc = 1;
		goto out;
	}
	keydata->version = arg.ver;
	keydata->flags = 0;
	keydata->sesskey_len = sess_key.length;
	keydata->secblob_len = secblob.length;
	memcpy(&(keydata->data), sess_key.data, sess_key.length);
	memcpy(&(keydata->data) + keydata->sesskey_len,
	       secblob.data, secblob.length);

	/* setup key */
	rc = keyctl_instantiate(key, keydata, datalen, 0);
	if (rc == -1) {
		syslog(LOG_ERR, "keyctl_instantiate: %s", strerror(errno));
		goto out;
	}

	/* BB: maybe we need use timeout for key: for example no more then
	 * ticket lifietime? */
	/* keyctl_set_timeout( key, 60); */
out:
	/*
	 * on error, negatively instantiate the key ourselves so that we can
	 * make sure the kernel doesn't hang it off of a searchable keyring
	 * and interfere with the next attempt to instantiate the key.
	 */
	if (rc != 0 && key == 0) {
		syslog(LOG_DEBUG, "Negating key");
		keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
	}
	data_blob_free(&secblob);
	data_blob_free(&sess_key);
	SAFE_FREE(ccname);
	SAFE_FREE(arg.hostname);
	SAFE_FREE(arg.ip);
	SAFE_FREE(arg.username);
	SAFE_FREE(keydata);
	syslog(LOG_DEBUG, "Exit status %ld", rc);
	return rc;
}