+ lgss_release_cred(cred);
+ return rc;
+}
+
+static int lgssc_kr_negotiate_manual(key_serial_t keyid, struct lgss_cred *cred,
+ struct keyring_upcall_param *kup)
+{
+ struct lgss_nego_data lnd;
+ OM_uint32 min_stat;
+ int rc;
+
+retry:
+ memset(&lnd, 0, sizeof(lnd));
+
+ rc = lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid);
+ if (rc) {
+ logmsg(LL_ERR, "key %08x: failed to construct service "
+ "string\n", keyid);
+ error_kernel_key(keyid, -EACCES, 0);
+ goto out_cred;
+ }
+
+ rc = lgss_using_cred(cred);
+ if (rc) {
+ logmsg(LL_ERR, "key %08x: can't use cred\n", keyid);
+ error_kernel_key(keyid, -EACCES, 0);
+ goto out_cred;
+ }
+
+ rc = lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n);
+ if (rc) {
+ logmsg(LL_ERR, "key %08x: failed to initialize "
+ "negotiation data\n", keyid);
+ error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+ goto out_cred;
+ }
+
+ /*
+ * Handles the negotiation but then calls lgss_validate to make sure
+ * the token is valid. It also populates the lnd_ctx_token for the
+ * update to the kernel key
+ */
+ rc = lgssc_negotiation_manual(&lnd, cred);
+ if (rc == -EAGAIN) {
+ logmsg(LL_ERR, "Failed negotiation must retry\n");
+ goto retry;
+
+ } else if (rc) {
+ logmsg(LL_ERR, "key %08x: failed to negotiate\n", keyid);
+ error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+ goto out;
+ }
+
+ rc = update_kernel_key(keyid, &lnd, &lnd.lnd_ctx_token);
+ if (rc)
+ goto out;
+
+ logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
+ keyid, kup->kup_uid);
+out:
+ if (lnd.lnd_ctx_token.length != 0)
+ gss_release_buffer(&min_stat, &lnd.lnd_ctx_token);
+
+ lgssc_fini_nego_data(&lnd);
+
+out_cred:
+ lgss_release_cred(cred);
+ return rc;
+}
+
+/*
+ * note we inherited assumed authority from parent process
+ */
+static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
+ struct keyring_upcall_param *kup)
+{
+ int rc;
+
+ logmsg(LL_TRACE, "child start on behalf of key %08x: "
+ "cred %p, uid %u, svc %u, nid %"PRIx64", uids: %u:%u/%u:%u\n",
+ keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
+ kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
+
+ switch (cred->lc_mech->lmt_mech_n) {
+ case LGSS_MECH_NULL:
+ case LGSS_MECH_SK:
+ rc = lgssc_kr_negotiate_manual(keyid, cred, kup);
+ break;
+ case LGSS_MECH_KRB5:
+ default:
+ rc = lgssc_kr_negotiate_krb(keyid, cred, kup);
+ break;
+ }
+
+ return rc;