/* * 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; }
/* * 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; }
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; }
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; }
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() */
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; }