Example #1
0
krb5_error_code
kg_init_name(krb5_context context,
             krb5_principal principal,
             krb5_authdata_context ad_context,
             krb5_flags flags,
             krb5_gss_name_t *ret_name)
{
    krb5_error_code code;
    krb5_gss_name_t name;

    *ret_name = NULL;

    assert(principal != NULL);

    if (principal == NULL)
        return EINVAL;

    name = xmalloc(sizeof(krb5_gss_name_rec));
    if (name == NULL)
        return ENOMEM;

    memset(name, 0, sizeof(krb5_gss_name_rec));

    code = k5_mutex_init(&name->lock);
    if (code != 0)
        goto cleanup;

    if ((flags & KG_INIT_NAME_NO_COPY) == 0) {
        code = krb5_copy_principal(context, principal, &name->princ);
        if (code != 0)
            goto cleanup;

        if (ad_context != NULL) {
            code = krb5_authdata_context_copy(context,
                                              ad_context,
                                              &name->ad_context);
            if (code != 0)
                goto cleanup;
        }
    } else {
        name->princ = principal;
        name->ad_context = ad_context;
    }

    if ((flags & KG_INIT_NAME_INTERN) &&
        !kg_save_name((gss_name_t)name)) {
        code = G_VALIDATE_FAILED;
        goto cleanup;
    }

    *ret_name = name;

cleanup:
    if (code != 0)
        kg_release_name(context, 0, &name);

    return code;
}
Example #2
0
static errcode_t rw_setup(profile_t profile)
{
   	prf_file_t	file;
	errcode_t	retval = 0;

	if (!profile)
		return PROF_NO_PROFILE;

	if (profile->magic != PROF_MAGIC_PROFILE)
		return PROF_MAGIC_PROFILE;

	file = profile->first_file;

	retval = profile_lock_global();
	if (retval)
	    return retval;

	/* Don't update the file if we've already made modifications */
	if (file->data->flags & PROFILE_FILE_DIRTY) {
	    profile_unlock_global();
	    return 0;
	}

	if ((file->data->flags & PROFILE_FILE_SHARED) != 0) {
	    prf_data_t new_data;
	    new_data = profile_make_prf_data(file->data->filespec);
	    if (new_data == NULL) {
		retval = ENOMEM;
	    } else {
		retval = k5_mutex_init(&new_data->lock);
		if (retval == 0) {
		    new_data->root = NULL;
		    new_data->flags = file->data->flags & ~PROFILE_FILE_SHARED;
		    new_data->timestamp = 0;
		    new_data->upd_serial = file->data->upd_serial;
		}
	    }

	    if (retval != 0) {
		profile_unlock_global();
		free(new_data);
		return retval;
	    }
	    profile_dereference_data_locked(file->data);
	    file->data = new_data;
	}

	profile_unlock_global();
	retval = profile_update_file(file);

	return retval;
}
Example #3
0
File: threads.c Project: WeiY/krb5
/* Mutex allocation functions, for use in plugins that may not know
   what options a given set of libraries was compiled with.  */
int KRB5_CALLCONV
krb5int_mutex_alloc (k5_mutex_t **m)
{
    k5_mutex_t *ptr;
    int err;

    ptr = malloc (sizeof (k5_mutex_t));
    if (ptr == NULL)
        return ENOMEM;
    err = k5_mutex_init (ptr);
    if (err) {
        free (ptr);
        return err;
    }
    *m = ptr;
    return 0;
}
Example #4
0
/*ARGSUSED*/
krb5_error_code krb5_rc_resolve_type(krb5_context context, krb5_rcache *id,
				     char *type)
{
    struct krb5_rc_typelist *t;
    krb5_error_code err;
    err = k5_mutex_lock(&rc_typelist_lock);
    if (err)
	return err;
    for (t = typehead;t && strcmp(t->ops->type,type);t = t->next)
	;
    if (!t) {
	k5_mutex_unlock(&rc_typelist_lock);
	return KRB5_RC_TYPE_NOTFOUND;
    }
    /* allocate *id? nah */
    (*id)->ops = t->ops;
    k5_mutex_unlock(&rc_typelist_lock);
    return k5_mutex_init(&(*id)->lock);
}
Example #5
0
/* Convert a JSON value to a krb5 GSS name or to NULL. */
static int
json_to_kgname(krb5_context context, k5_json_value v,
               krb5_gss_name_t *name_out)
{
    k5_json_array array;
    krb5_gss_name_t name = NULL;

    *name_out = NULL;
    if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
        return 0;
    if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
        return -1;
    array = v;
    if (k5_json_array_length(array) != 3)
        return -1;
    name = calloc(1, sizeof(*name));
    if (name == NULL)
        return -1;
    if (k5_mutex_init(&name->lock)) {
        free(name);
        return -1;
    }

    if (json_to_principal(context, k5_json_array_get(array, 0), &name->princ))
        goto invalid;
    if (json_to_optional_string(k5_json_array_get(array, 1), &name->service))
        goto invalid;
    if (json_to_optional_string(k5_json_array_get(array, 2), &name->host))
        goto invalid;

    *name_out = name;
    return 0;

invalid:
    kg_release_name(context, &name);
    return -1;
}
Example #6
0
/*
 * This function reads the parameters from the krb5.conf file. The
 * parameters read here are DAL-LDAP specific attributes. Some of
 * these are ldap_server ....
 */
krb5_error_code
krb5_ldap_read_server_params(krb5_context context, char *conf_section,
                             int srv_type)
{
    char *servers, *save_ptr, *item;
    const char *delims = "\t\n\f\v\r ,", *name;
    krb5_error_code ret = 0;
    kdb5_dal_handle *dal_handle = context->dal_handle;
    krb5_ldap_context *ldap_context = dal_handle->db_context;

    /* copy the conf_section into ldap_context for later use */
    if (conf_section != NULL) {
        ldap_context->conf_section = strdup(conf_section);
        if (ldap_context->conf_section == NULL)
            return ENOMEM;
    }

    /* This mutex is used in the LDAP connection pool. */
    if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0)
        return KRB5_KDB_SERVER_INTERNAL_ERR;

    /* Read the maximum number of LDAP connections per server. */
    if (ldap_context->max_server_conns == 0) {
        ret = prof_get_integer_def(context, conf_section,
                                   KRB5_CONF_LDAP_CONNS_PER_SERVER,
                                   DEFAULT_CONNS_PER_SERVER,
                                   &ldap_context->max_server_conns);
        if (ret)
            return ret;
    }

    if (ldap_context->max_server_conns < 2) {
        k5_setmsg(context, EINVAL,
                  _("Minimum connections required per server is 2"));
        return EINVAL;
    }

    /* Read the DN used to connect to the LDAP server. */
    if (ldap_context->bind_dn == NULL) {
        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_DN,
                          KRB5_CONF_LDAP_KADMIND_DN);
        ret = prof_get_string_def(context, conf_section, name,
                                  &ldap_context->bind_dn);
        if (ret)
            return ret;
    }

    /* Read the filename containing stashed DN passwords. */
    if (ldap_context->service_password_file == NULL) {
        ret = prof_get_string_def(context, conf_section,
                                  KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE,
                                  &ldap_context->service_password_file);
        if (ret)
            return ret;
    }

    if (ldap_context->sasl_mech == NULL) {
        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_MECH,
                          KRB5_CONF_LDAP_KADMIND_SASL_MECH);
        ret = prof_get_string_def(context, conf_section, name,
                                  &ldap_context->sasl_mech);
        if (ret)
            return ret;
    }

    if (ldap_context->sasl_authcid == NULL) {
        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHCID,
                          KRB5_CONF_LDAP_KADMIND_SASL_AUTHCID);
        ret = prof_get_string_def(context, conf_section, name,
                                  &ldap_context->sasl_authcid);
        if (ret)
            return ret;
    }

    if (ldap_context->sasl_authzid == NULL) {
        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHZID,
                          KRB5_CONF_LDAP_KADMIND_SASL_AUTHZID);
        ret = prof_get_string_def(context, conf_section, name,
                                  &ldap_context->sasl_authzid);
        if (ret)
            return ret;
    }

    if (ldap_context->sasl_realm == NULL) {
        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_REALM,
                          KRB5_CONF_LDAP_KADMIND_SASL_REALM);
        ret = prof_get_string_def(context, conf_section, name,
                                  &ldap_context->sasl_realm);
        if (ret)
            return ret;
    }

    /* Read the LDAP server URL list. */
    if (ldap_context->server_info_list == NULL) {
        ret = profile_get_string(context->profile, KDB_MODULE_SECTION,
                                 conf_section, KRB5_CONF_LDAP_SERVERS, NULL,
                                 &servers);
        if (ret)
            return attr_read_error(context, ret, KRB5_CONF_LDAP_SERVERS);

        if (servers == NULL) {
            ret = add_server_entry(context, "ldapi://");
            if (ret)
                return ret;
        } else {
            item = strtok_r(servers, delims, &save_ptr);
            while (item != NULL) {
                ret = add_server_entry(context, item);
                if (ret) {
                    profile_release_string(servers);
                    return ret;
                }
                item = strtok_r(NULL, delims, &save_ptr);
            }
            profile_release_string(servers);
        }
    }

    ret = prof_get_boolean_def(context, conf_section,
                               KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE,
                               &ldap_context->disable_last_success);
    if (ret)
        return ret;

    return prof_get_boolean_def(context, conf_section,
                                KRB5_CONF_DISABLE_LOCKOUT, FALSE,
                                &ldap_context->disable_lockout);
}
Example #7
0
OM_uint32
kg_compose_deleg_cred(OM_uint32 *minor_status,
                      krb5_gss_cred_id_t impersonator_cred,
                      krb5_creds *subject_creds,
                      OM_uint32 time_req,
                      krb5_gss_cred_id_t *output_cred,
                      OM_uint32 *time_rec,
                      krb5_context context)
{
    OM_uint32 major_status;
    krb5_error_code code;
    krb5_gss_cred_id_t cred = NULL;

    *output_cred = NULL;
    k5_mutex_assert_locked(&impersonator_cred->lock);

    if (!kg_is_initiator_cred(impersonator_cred) ||
        impersonator_cred->name == NULL ||
        impersonator_cred->impersonator != NULL) {
        code = G_BAD_USAGE;
        goto cleanup;
    }

    assert(impersonator_cred->name->princ != NULL);

    assert(subject_creds != NULL);
    assert(subject_creds->client != NULL);

    cred = xmalloc(sizeof(*cred));
    if (cred == NULL) {
        code = ENOMEM;
        goto cleanup;
    }
    memset(cred, 0, sizeof(*cred));

    code = k5_mutex_init(&cred->lock);
    if (code != 0)
        goto cleanup;

    cred->usage = GSS_C_INITIATE;

    cred->tgt_expire = subject_creds->times.endtime;

    code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0,
                        &cred->name);
    if (code != 0)
        goto cleanup;

    code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
    if (code != 0)
        goto cleanup;
    cred->destroy_ccache = 1;

    code = krb5_cc_initialize(context, cred->ccache, subject_creds->client);
    if (code != 0)
        goto cleanup;

    /*
     * Only return a "proxy" credential for use with constrained
     * delegation if the subject credentials are forwardable.
     * Submitting non-forwardable credentials to the KDC for use
     * with constrained delegation will only return an error.
     */
    if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) {
        code = make_proxy_cred(context, cred, impersonator_cred);
        if (code != 0)
            goto cleanup;
    }

    code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
    if (code != 0)
        goto cleanup;

    if (time_rec != NULL) {
        krb5_timestamp now;

        code = krb5_timeofday(context, &now);
        if (code != 0)
            goto cleanup;

        *time_rec = cred->tgt_expire - now;
    }

    major_status = GSS_S_COMPLETE;
    *minor_status = 0;
    *output_cred = cred;

cleanup:
    if (code != 0) {
        *minor_status = code;
        major_status = GSS_S_FAILURE;
    }

    if (GSS_ERROR(major_status) && cred != NULL) {
        k5_mutex_destroy(&cred->lock);
        krb5_cc_destroy(context, cred->ccache);
        kg_release_name(context, &cred->name);
        xfree(cred);
    }

    return major_status;
}
Example #8
0
krb5_error_code
kg_init_name(krb5_context context, krb5_principal principal,
             char *service, char *host, krb5_authdata_context ad_context,
             krb5_flags flags, krb5_gss_name_t *ret_name)
{
    krb5_error_code code;
    krb5_gss_name_t name;

    *ret_name = NULL;

    assert(principal != NULL);

    if (principal == NULL)
        return EINVAL;

    name = xmalloc(sizeof(krb5_gss_name_rec));
    if (name == NULL)
        return ENOMEM;

    memset(name, 0, sizeof(krb5_gss_name_rec));

    code = k5_mutex_init(&name->lock);
    if (code != 0)
        goto cleanup;

    if ((flags & KG_INIT_NAME_NO_COPY) == 0) {
        code = krb5_copy_principal(context, principal, &name->princ);
        if (code != 0)
            goto cleanup;

        if (ad_context != NULL) {
            code = krb5_authdata_context_copy(context,
                                              ad_context,
                                              &name->ad_context);
            if (code != 0)
                goto cleanup;
        }

        code = ENOMEM;
        if (service != NULL) {
            name->service = strdup(service);
            if (name->service == NULL)
                goto cleanup;
        }
        if (host != NULL) {
            name->host = strdup(host);
            if (name->host == NULL)
                goto cleanup;
        }
        code = 0;
    } else {
        name->princ = principal;
        name->service = service;
        name->host = host;
        name->ad_context = ad_context;
    }

    *ret_name = name;

cleanup:
    if (code != 0)
        kg_release_name(context, &name);

    return code;
}
Example #9
0
errcode_t profile_open_file(const_profile_filespec_t filespec,
			    prf_file_t *ret_prof)
{
	prf_file_t	prf;
	errcode_t	retval;
	char		*home_env = 0;
	prf_data_t	data;
	char		*expanded_filename;

	retval = CALL_INIT_FUNCTION(profile_library_initializer);
	if (retval)
		return retval;

	scan_shared_trees_unlocked();

	prf = malloc(sizeof(struct _prf_file_t));
	if (!prf)
		return ENOMEM;
	memset(prf, 0, sizeof(struct _prf_file_t));
	prf->magic = PROF_MAGIC_FILE;

	if (filespec[0] == '~' && filespec[1] == '/') {
		home_env = getenv("HOME");
#ifdef HAVE_PWD_H
		if (home_env == NULL) {
		    uid_t uid;
		    struct passwd *pw, pwx;
		    char pwbuf[BUFSIZ];

		    uid = getuid();
		    if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
			&& pw != NULL && pw->pw_dir[0] != 0)
			home_env = pw->pw_dir;
		}
#endif
	}
	if (home_env) {
	    if (asprintf(&expanded_filename, "%s%s", home_env,
			 filespec + 1) < 0)
		expanded_filename = 0;
	} else
	    expanded_filename = strdup(filespec);
	if (expanded_filename == 0) {
	    free(prf);
	    return ENOMEM;
	}

	retval = k5_mutex_lock(&g_shared_trees_mutex);
	if (retval) {
	    free(expanded_filename);
	    free(prf);
	    scan_shared_trees_unlocked();
	    return retval;
	}
	scan_shared_trees_locked();
	for (data = g_shared_trees; data; data = data->next) {
	    if (!strcmp(data->filespec, expanded_filename)
		/* Check that current uid has read access.  */
		&& r_access(data->filespec))
		break;
	}
	if (data) {
	    data->refcount++;
	    (void) k5_mutex_unlock(&g_shared_trees_mutex);
	    retval = profile_update_file_data(data);
	    free(expanded_filename);
	    prf->data = data;
	    *ret_prof = prf;
	    scan_shared_trees_unlocked();
	    return retval;
	}
	(void) k5_mutex_unlock(&g_shared_trees_mutex);
	data = profile_make_prf_data(expanded_filename);
	if (data == NULL) {
	    free(prf);
	    free(expanded_filename);
	    return ENOMEM;
	}
	free(expanded_filename);
	prf->data = data;

	retval = k5_mutex_init(&data->lock);
	if (retval) {
	    free(data);
	    free(prf);
	    return retval;
	}

	retval = profile_update_file(prf);
	if (retval) {
		profile_close_file(prf);
		return retval;
	}

	retval = k5_mutex_lock(&g_shared_trees_mutex);
	if (retval) {
	    profile_close_file(prf);
	    scan_shared_trees_unlocked();
	    return retval;
	}
	scan_shared_trees_locked();
	data->flags |= PROFILE_FILE_SHARED;
	data->next = g_shared_trees;
	g_shared_trees = data;
	scan_shared_trees_locked();
	(void) k5_mutex_unlock(&g_shared_trees_mutex);

	*ret_prof = prf;
	return 0;
}
Example #10
0
/* Convert a JSON array value to a krb5 GSS credential. */
static int
json_to_kgcred(krb5_context context, k5_json_array array,
               krb5_gss_cred_id_t *cred_out)
{
    krb5_gss_cred_id_t cred;
    k5_json_number n;
    k5_json_bool b;
    krb5_boolean is_new;
    OM_uint32 tmp;

    *cred_out = NULL;
    if (k5_json_array_length(array) != 14)
        return -1;

    cred = calloc(1, sizeof(*cred));
    if (cred == NULL)
        return -1;
    if (k5_mutex_init(&cred->lock)) {
        free(cred);
        return -1;
    }

    n = check_element(array, 0, K5_JSON_TID_NUMBER);
    if (n == NULL)
        goto invalid;
    cred->usage = k5_json_number_value(n);

    if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name))
        goto invalid;

    if (json_to_principal(context, k5_json_array_get(array, 2),
                          &cred->impersonator))
        goto invalid;

    b = check_element(array, 3, K5_JSON_TID_BOOL);
    if (b == NULL)
        goto invalid;
    cred->default_identity = k5_json_bool_value(b);

    b = check_element(array, 4, K5_JSON_TID_BOOL);
    if (b == NULL)
        goto invalid;
    cred->iakerb_mech = k5_json_bool_value(b);

    if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab))
        goto invalid;

    if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache))
        goto invalid;

    if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache,
                       &is_new))
        goto invalid;
    cred->destroy_ccache = is_new;

    if (json_to_keytab(context, k5_json_array_get(array, 8),
                       &cred->client_keytab))
        goto invalid;

    b = check_element(array, 9, K5_JSON_TID_BOOL);
    if (b == NULL)
        goto invalid;
    cred->have_tgt = k5_json_bool_value(b);

    n = check_element(array, 10, K5_JSON_TID_NUMBER);
    if (n == NULL)
        goto invalid;
    cred->expire = k5_json_number_value(n);

    n = check_element(array, 11, K5_JSON_TID_NUMBER);
    if (n == NULL)
        goto invalid;
    cred->refresh_time = k5_json_number_value(n);

    if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes))
        goto invalid;

    if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password))
        goto invalid;

    *cred_out = cred;
    return 0;

invalid:
    (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred);
    return -1;
}
Example #11
0
/* Load a dynamic profile module as specified by modspec and create a vtable
 * profile for it in *ret_profile. */
static errcode_t
init_load_module(const char *modspec, profile_t *ret_profile)
{
    char *modpath = NULL, *residual = NULL;
    struct errinfo einfo = { 0 };
    prf_lib_handle_t lib_handle = NULL;
    struct plugin_file_handle *plhandle = NULL;
    void *cbdata = NULL, (*fptr)();
    int have_lock = 0, have_cbdata = 0;
    struct profile_vtable vtable = { 1 };  /* Set minor_ver to 1, rest null. */
    errcode_t err;
    profile_module_init_fn initfn;

    err = parse_modspec(modspec, &modpath, &residual);
    if (err)
        goto cleanup;

    /* Allocate a reference-counted library handle container. */
    lib_handle = malloc(sizeof(*lib_handle));
    if (lib_handle == NULL)
        goto cleanup;
    err = k5_mutex_init(&lib_handle->lock);
    if (err)
        goto cleanup;
    have_lock = 1;

    /* Open the module and get its initializer. */
    err = krb5int_open_plugin(modpath, &plhandle, &einfo);
    if (err)
        goto cleanup;
    err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,
                                  &einfo);
    if (err == ENOENT)
        err = PROF_MODULE_INVALID;
    if (err)
        goto cleanup;

    /* Get the profile vtable and callback data pointer. */
    initfn = (profile_module_init_fn)fptr;
    err = (*initfn)(residual, &vtable, &cbdata);
    if (err)
        goto cleanup;
    have_cbdata = 1;

    /* Create a vtable profile with the information obtained. */
    lib_handle->plugin_handle = plhandle;
    lib_handle->refcount = 1;
    err = init_module(&vtable, cbdata, lib_handle, ret_profile);

cleanup:
    free(modpath);
    free(residual);
    k5_clear_error(&einfo);
    if (err) {
        if (have_cbdata && vtable.cleanup)
            vtable.cleanup(cbdata);
        if (have_lock)
            k5_mutex_destroy(&lib_handle->lock);
        free(lib_handle);
        if (plhandle)
            krb5int_close_plugin(plhandle);
    }
    return err;
}
Example #12
0
krb5_error_code KRB5_CALLCONV
krb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)
{
    krb5_mkt_data *data = 0;
    krb5_mkt_list_node *list;
    krb5_error_code err = 0;

    /* First determine if a memory keytab of this name already exists */
    err = KTGLOCK;
    if (err)
	return(err);

    for (list = krb5int_mkt_list; list; list = list->next)
    {
	if (strcmp(name,KTNAME(list->keytab)) == 0) {
	    /* Found */
	    *id = list->keytab;
	    goto done;
	}
    }

    /* We will now create the new key table with the specified name.
     * We do not drop the global lock, therefore the name will indeed
     * be unique when we add it.
     */

    if ((list = (krb5_mkt_list_node *)malloc(sizeof(krb5_mkt_list_node))) == NULL) {
	err = ENOMEM;
	goto done;
    }

    if ((list->keytab = (krb5_keytab)malloc(sizeof(struct _krb5_kt))) == NULL) {
	free(list);
	err = ENOMEM;
	goto done;
    }

    list->keytab->ops = &krb5_mkt_ops;
    if ((data = (krb5_mkt_data *)malloc(sizeof(krb5_mkt_data))) == NULL) {
	free(list->keytab);
	free(list);
	err = ENOMEM;
	goto done;
    }
    data->name = NULL;

    err = k5_mutex_init(&data->lock);
    if (err) {
	free(data);
	free(list->keytab);
	free(list);
	goto done;
    }

    if ((data->name = strdup(name)) == NULL) {
	k5_mutex_destroy(&data->lock);
	free(data);
	free(list->keytab);
	free(list);
	err = ENOMEM;
	goto done;
    }

    data->link = NULL;
    data->refcount = 0;
    list->keytab->data = (krb5_pointer)data;
    list->keytab->magic = KV5M_KEYTAB;

    list->next = krb5int_mkt_list;
    krb5int_mkt_list = list;

    *id = list->keytab;

  done:
    err = KTLOCK(*id);
    if (err) {
	k5_mutex_destroy(&data->lock);
	if (data && data->name)
		free(data->name);
	free(data);
	if (list && list->keytab)
		free(list->keytab);
	free(list);
    } else {
	KTREFCNT(*id)++;
	KTUNLOCK(*id);
    }

    KTGUNLOCK;
    return(err);
}