Beispiel #1
0
/*
 * 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)
{
        struct lgss_nego_data   lnd;
        gss_buffer_desc         token = GSS_C_EMPTY_BUFFER;
        OM_uint32               min_stat;
        int                     rc = -1;

        logmsg(LL_TRACE, "child start on behalf of key %08x: "
               "cred %p, uid %u, svc %u, nid %llx, 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);

        if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
                logmsg(LL_ERR, "key %08x: failed to construct service "
                       "string\n", keyid);
                error_kernel_key(keyid, -EACCES, 0);
                goto out_cred;
        }

        if (lgss_using_cred(cred)) {
                logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
                error_kernel_key(keyid, -EACCES, 0);
                goto out_cred;
        }

        if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
                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;
        }

        rc = lgssc_negotiation(&lnd);
        if (rc) {
                logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
                error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
                goto out;
        }

        rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
        if (rc) {
                logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
                error_kernel_key(keyid, rc, lnd.lnd_gss_err);
                goto out;
        }

        rc = update_kernel_key(keyid,  &lnd, &token);
        if (rc)
                goto out;

        rc = 0;
        logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
               keyid, kup->kup_uid);
out:
        if (token.length != 0)
                gss_release_buffer(&min_stat, &token);

        lgssc_fini_nego_data(&lnd);

out_cred:
        lgss_release_cred(cred);
        return rc;
}
Beispiel #2
0
/*
 * this code uses the userland rpcsec gss library to create a krb5
 * context on behalf of the kernel
 */
static void
process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
		    char *service)
{
	CLIENT			*rpc_clnt = NULL;
	AUTH			*auth = NULL;
	struct authgss_private_data pd;
	gss_buffer_desc		token;
	int			err, downcall_err = -EACCES;
	OM_uint32		maj_stat, min_stat, lifetime_rec;
	pid_t			pid, childpid = -1;
	gss_name_t		gacceptor = GSS_C_NO_NAME;
	gss_OID			mech;
	gss_buffer_desc		acceptor  = {0};

	token.length = 0;
	token.value = NULL;
	memset(&pd, 0, sizeof(struct authgss_private_data));

	/*
	 * If "service" is specified, then the kernel is indicating that
	 * we must use machine credentials for this request.  (Regardless
	 * of the uid value or the setting of root_uses_machine_creds.)
	 * If the service value is "*", then any service name can be used.
	 * Otherwise, it specifies the service name that should be used.
	 * (For now, the values of service will only be "*" or "nfs".)
	 *
	 * Restricting gssd to use "nfs" service name is needed for when
	 * the NFS server is doing a callback to the NFS client.  In this
	 * case, the NFS server has to authenticate itself as "nfs" --
	 * even if there are other service keys such as "host" or "root"
	 * in the keytab.
	 *
	 * Another case when the kernel may specify the service attribute
	 * is when gssd is being asked to create the context for a
	 * SETCLIENT_ID operation.  In this case, machine credentials
	 * must be used for the authentication.  However, the service name
	 * used for this case is not important.
	 *
	 */
	if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
				service == NULL)) {

		/* already running as uid 0 */
		if (uid == 0)
			goto no_fork;

		pid = fork();
		switch(pid) {
		case 0:
			/* Child: fall through to rest of function */
			childpid = getpid();
			unsetenv("KRB5CCNAME");
			printerr(2, "CHILD forked pid %d \n", childpid);
			break;
		case -1:
			/* fork() failed! */
			printerr(0, "WARNING: unable to fork() to handle"
				"upcall: %s\n", strerror(errno));
			return;
		default:
			/* Parent: just wait on child to exit and return */
			do {
				pid = wait(&err);
			} while(pid == -1 && errno != -ECHILD);

			if (WIFSIGNALED(err))
				printerr(0, "WARNING: forked child was killed"
					 "with signal %d\n", WTERMSIG(err));
			return;
		}
no_fork:

		auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err,
						&err, &rpc_clnt);
		if (err)
			goto out_return_error;
	}
	if (auth == NULL) {
		if (uid == 0 && (root_uses_machine_creds == 1 ||
				service != NULL)) {
			auth =	krb5_use_machine_creds(clp, uid, tgtname,
							service, &rpc_clnt);
			if (auth == NULL)
				goto out_return_error;
		} else {
			/* krb5_not_machine_creds logs the error */
			goto out_return_error;
		}
	}

	if (!authgss_get_private_data(auth, &pd)) {
		printerr(1, "WARNING: Failed to obtain authentication "
			    "data for user with uid %d for server %s\n",
			 uid, clp->servername);
		goto out_return_error;
	}

	/* Grab the context lifetime and acceptor name out of the ctx. */
	maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor,
				       &lifetime_rec, &mech, NULL, NULL, NULL);

	if (maj_stat != GSS_S_COMPLETE) {
		printerr(1, "WARNING: Failed to inquire context "
			    "maj_stat (0x%x)\n", maj_stat);
		lifetime_rec = 0;
	} else {
		get_hostbased_client_buffer(gacceptor, mech, &acceptor);
		gss_release_name(&min_stat, &gacceptor);
	}

	/*
	 * The serialization can mean turning pd.pd_ctx into a lucid context. If
	 * that happens then the pd.pd_ctx will be unusable, so we must never
	 * try to use it after this point.
	 */
	if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
		printerr(1, "WARNING: Failed to serialize krb5 context for "
			    "user with uid %d for server %s\n",
			 uid, clp->servername);
		goto out_return_error;
	}

	do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor);

out:
	gss_release_buffer(&min_stat, &acceptor);
	if (token.value)
		free(token.value);
#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
	if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0)
		authgss_free_private_data(&pd);
#endif
	if (auth)
		AUTH_DESTROY(auth);
	if (rpc_clnt)
		clnt_destroy(rpc_clnt);

	pid = getpid();
	if (pid == childpid)
		exit(0);
	else
		return;

out_return_error:
	do_error_downcall(fd, uid, downcall_err);
	goto out;
}