/* * Copyright (c) 2005 Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* $Id$ */ #include #include static CRITICAL_SECTION cs_ident; hashtable * kcdb_identities_namemap = NULL; khm_int32 kcdb_n_identities = 0; kcdb_identity * kcdb_identities = NULL; kcdb_identity * kcdb_def_identity = NULL; khm_handle kcdb_ident_sub = NULL; /* identity provider */ khm_int32 kcdb_ident_cred_type = KCDB_CREDTYPE_INVALID; /* primary credentials type */ khm_ui_4 kcdb_ident_refresh_cycle = 0; khm_boolean kcdb_checked_config = FALSE; khm_boolean kcdb_checking_config = FALSE; KHMEXP khm_boolean KHMAPI kcdb_identity_is_equal(khm_handle identity1, khm_handle identity2) { return (identity1 == identity2); } KHMEXP khm_int32 KHMAPI kcdb_identity_set_provider(khm_handle sub) { EnterCriticalSection(&cs_ident); if (sub != kcdb_ident_sub) { if(kcdb_ident_sub != NULL) { kmq_post_sub_msg(kcdb_ident_sub, KMSG_IDENT, KMSG_IDENT_EXIT, 0, 0); kmq_delete_subscription(kcdb_ident_sub); } kcdb_ident_sub = sub; if (kcdb_ident_sub) kmq_post_sub_msg(kcdb_ident_sub, KMSG_IDENT, KMSG_IDENT_INIT, 0, 0); } LeaveCriticalSection(&cs_ident); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_provider(khm_handle * sub) { khm_int32 rv = KHM_ERROR_SUCCESS; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) rv = KHM_ERROR_SUCCESS; else rv = KHM_ERROR_NOT_FOUND; if(sub != NULL) *sub = kcdb_ident_sub; LeaveCriticalSection(&cs_ident); return rv; } KHMEXP khm_int32 KHMAPI kcdb_identity_set_type(khm_int32 cred_type) { EnterCriticalSection(&cs_ident); kcdb_ident_cred_type = cred_type; LeaveCriticalSection(&cs_ident); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_type(khm_int32 * ptype) { if (!ptype) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); *ptype = kcdb_ident_cred_type; LeaveCriticalSection(&cs_ident); if (*ptype >= 0) return KHM_ERROR_SUCCESS; else return KHM_ERROR_NOT_FOUND; } /* message completion routine */ void kcdbint_ident_msg_completion(kmq_message * m) { kcdb_identity_release(m->vparam); } void kcdbint_ident_add_ref(const void * key, void * vid) { /* References in the hashtable are not refcounted */ // kcdb_identity_hold(vid); } void kcdbint_ident_del_ref(const void * key, void * vid) { /* References in the hashtable are not refcounted */ // kcdb_identity_release(vid); } void kcdbint_ident_init(void) { InitializeCriticalSection(&cs_ident); kcdb_identities_namemap = hash_new_hashtable( KCDB_IDENT_HASHTABLE_SIZE, hash_string, hash_string_comp, kcdbint_ident_add_ref, kcdbint_ident_del_ref); } void kcdbint_ident_exit(void) { EnterCriticalSection(&cs_ident); hash_del_hashtable(kcdb_identities_namemap); LeaveCriticalSection(&cs_ident); DeleteCriticalSection(&cs_ident); } /* NOT called with cs_ident held */ KHMEXP khm_boolean KHMAPI kcdb_identity_is_valid_name(const wchar_t * name) { khm_int32 rv; /* special case. Note since the string we are comparing with is of a known length we don't need to check the length of name. */ if (!wcscmp(name, L"_Schema")) return FALSE; rv = kcdb_identpro_validate_name(name); if(rv == KHM_ERROR_NO_PROVIDER || rv == KHM_ERROR_NOT_IMPLEMENTED) return TRUE; else return KHM_SUCCEEDED(rv); } KHMEXP khm_int32 KHMAPI kcdb_identity_create(const wchar_t *name, khm_int32 flags, khm_handle * result) { kcdb_identity * id = NULL; kcdb_identity * id_tmp = NULL; size_t namesize; if(!result || !name) return KHM_ERROR_INVALID_PARAM; *result = NULL; /* is it there already? */ EnterCriticalSection(&cs_ident); id = hash_lookup(kcdb_identities_namemap, (void *) name); if(id) kcdb_identity_hold((khm_handle) id); LeaveCriticalSection(&cs_ident); if(id) { *result = (khm_handle) id; return KHM_ERROR_SUCCESS; } else if(!(flags & KCDB_IDENT_FLAG_CREATE)) { return KHM_ERROR_NOT_FOUND; } flags &= ~KCDB_IDENT_FLAG_CREATE; /* nope. create it */ if((flags & ~KCDB_IDENT_FLAGMASK_RDWR) || (flags & (KCDB_IDENT_FLAG_DEFAULT | KCDB_IDENT_FLAG_SEARCHABLE | KCDB_IDENT_FLAG_STICKY))) { /* can't specify this flag in create */ return KHM_ERROR_INVALID_PARAM; } if(!kcdb_identity_is_valid_name(name)) { return KHM_ERROR_INVALID_NAME; } /* we expect the following will succeed since the above test passed */ StringCbLength(name, KCDB_IDENT_MAXCB_NAME, &namesize); namesize += sizeof(wchar_t); id = PMALLOC(sizeof(kcdb_identity)); ZeroMemory(id, sizeof(kcdb_identity)); id->magic = KCDB_IDENT_MAGIC; id->name = PMALLOC(namesize); StringCbCopy(id->name, namesize, name); id->flags = (flags & KCDB_IDENT_FLAGMASK_RDWR); id->flags |= KCDB_IDENT_FLAG_ACTIVE; LINIT(id); EnterCriticalSection(&cs_ident); id_tmp = hash_lookup(kcdb_identities_namemap, (void *) id->name); if(id_tmp) { /* lost a race */ kcdb_identity_hold((khm_handle) id_tmp); *result = (khm_handle) id_tmp; PFREE(id->name); PFREE(id); id = NULL; } else { khm_handle h_cfg; kcdb_identity_hold((khm_handle) id); hash_add(kcdb_identities_namemap, (void *) id->name, (void *) id); LPUSH(&kcdb_identities, id); if(KHM_SUCCEEDED(kcdb_identity_get_config((khm_handle) id, 0, &h_cfg))) { /* don't need to set the KCDB_IDENT_FLAG_CONFIG flags since kcdb_identity_get_config() sets it for us. */ khm_int32 sticky; if (KHM_SUCCEEDED(khc_read_int32(h_cfg, L"Sticky", &sticky)) && sticky) { id->flags |= KCDB_IDENT_FLAG_STICKY; } khc_close_space(h_cfg); } } LeaveCriticalSection(&cs_ident); if(id != NULL) { *result = (khm_handle) id; kcdb_identpro_notify_create((khm_handle) id); kcdbint_ident_post_message(KCDB_OP_INSERT, id); } return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_delete(khm_handle vid) { kcdb_identity * id; khm_int32 code = KHM_ERROR_SUCCESS; EnterCriticalSection(&cs_ident); if(!kcdb_is_identity(vid)) { code = KHM_ERROR_INVALID_PARAM; goto _exit; } id = (kcdb_identity *) vid; if (kcdb_is_active_identity(vid)) { id->flags &= ~KCDB_IDENT_FLAG_ACTIVE; hash_del(kcdb_identities_namemap, (void *) id->name); LeaveCriticalSection(&cs_ident); kcdbint_ident_post_message(KCDB_OP_DELETE, id); /* Once everybody finishes dealing with the identity deletion, we will get called again. */ return KHM_ERROR_SUCCESS; } else if (id->refcount == 0) { /* If the identity is not active, it is not in the hashtable either */ LDELETE(&kcdb_identities, id); if (id->name) PFREE(id->name); PFREE(id); } /* else, we have an identity that is not active, but has outstanding references. We have to wait until those references are freed. Once they are released, kcdb_identity_delete() will be called again. */ #if 0 EnterCriticalSection(&cs_ident); if(id->refcount == 0) { /*TODO: free up the identity */ } #endif _exit: LeaveCriticalSection(&cs_ident); return code; } KHMEXP khm_int32 KHMAPI kcdb_identity_set_flags(khm_handle vid, khm_int32 flag, khm_int32 mask) { kcdb_identity * id; khm_int32 oldflags; khm_int32 newflags; khm_int32 delta = 0; khm_int32 rv; if (mask == 0) return KHM_ERROR_SUCCESS; if(!kcdb_is_active_identity(vid)) return KHM_ERROR_INVALID_PARAM; id = (kcdb_identity *) vid; flag &= mask; if((mask & ~KCDB_IDENT_FLAGMASK_RDWR) || ((flag & KCDB_IDENT_FLAG_INVALID) && (flag & KCDB_IDENT_FLAG_VALID))) return KHM_ERROR_INVALID_PARAM; if((mask & KCDB_IDENT_FLAG_DEFAULT) && (flag & KCDB_IDENT_FLAG_DEFAULT)) { /* kcdb_identity_set_default already does checking for redundant transitions */ rv = kcdb_identity_set_default(vid); if(KHM_FAILED(rv)) return rv; mask &= ~KCDB_IDENT_FLAG_DEFAULT; flag &= ~KCDB_IDENT_FLAG_DEFAULT; } EnterCriticalSection(&cs_ident); if(mask & KCDB_IDENT_FLAG_SEARCHABLE) { if(!(flag & KCDB_IDENT_FLAG_SEARCHABLE)) { if(id->flags & KCDB_IDENT_FLAG_SEARCHABLE) { LeaveCriticalSection(&cs_ident); rv = kcdb_identpro_set_searchable(vid, FALSE); EnterCriticalSection(&cs_ident); if(rv == KHM_ERROR_NO_PROVIDER || KHM_SUCCEEDED(rv)) { id->flags &= ~KCDB_IDENT_FLAG_SEARCHABLE; delta |= KCDB_IDENT_FLAG_SEARCHABLE; } } } else { if(!(id->flags & KCDB_IDENT_FLAG_SEARCHABLE)) { LeaveCriticalSection(&cs_ident); rv = kcdb_identpro_set_searchable(vid, TRUE); EnterCriticalSection(&cs_ident); if(rv == KHM_ERROR_NO_PROVIDER || KHM_SUCCEEDED(rv)) { id->flags |= KCDB_IDENT_FLAG_SEARCHABLE; delta |= KCDB_IDENT_FLAG_SEARCHABLE; } } } flag &= ~KCDB_IDENT_FLAG_SEARCHABLE; mask &= ~KCDB_IDENT_FLAG_SEARCHABLE; } if (mask & KCDB_IDENT_FLAG_STICKY) { if ((flag ^ id->flags) & KCDB_IDENT_FLAG_STICKY) { khm_handle h_conf; if (KHM_SUCCEEDED(kcdb_identity_get_config(vid, KHM_FLAG_CREATE, &h_conf))) { khc_write_int32(h_conf, L"Sticky", !!(flag & KCDB_IDENT_FLAG_STICKY)); khc_close_space(h_conf); } id->flags = ((id->flags & ~KCDB_IDENT_FLAG_STICKY) | (flag & KCDB_IDENT_FLAG_STICKY)); delta |= KCDB_IDENT_FLAG_STICKY; } flag &= ~KCDB_IDENT_FLAG_STICKY; mask &= ~KCDB_IDENT_FLAG_STICKY; } /* deal with every other flag */ oldflags = id->flags; id->flags = (id->flags & ~mask) | (flag & mask); if (flag & KCDB_IDENT_FLAG_VALID) id->flags &= ~KCDB_IDENT_FLAG_INVALID; if (flag & KCDB_IDENT_FLAG_INVALID) id->flags &= ~KCDB_IDENT_FLAG_VALID; newflags = id->flags; LeaveCriticalSection(&cs_ident); delta |= newflags ^ oldflags; if((delta & KCDB_IDENT_FLAG_HIDDEN)) { kcdbint_ident_post_message( (newflags & KCDB_IDENT_FLAG_HIDDEN)?KCDB_OP_HIDE:KCDB_OP_UNHIDE, vid); } if((delta & KCDB_IDENT_FLAG_SEARCHABLE)) { kcdbint_ident_post_message( (newflags & KCDB_IDENT_FLAG_SEARCHABLE)?KCDB_OP_SETSEARCH:KCDB_OP_UNSETSEARCH, vid); } if(delta != 0) kcdbint_ident_post_message(KCDB_OP_MODIFY, vid); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_flags(khm_handle vid, khm_int32 * flags) { kcdb_identity * id; *flags = 0; if(!kcdb_is_active_identity(vid)) return KHM_ERROR_INVALID_PARAM; id = (kcdb_identity *) vid; *flags = id->flags; return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_name(khm_handle vid, wchar_t * buffer, khm_size * pcbsize) { size_t namesize; kcdb_identity * id; if(!kcdb_is_active_identity(vid) || !pcbsize) return KHM_ERROR_INVALID_PARAM; id = (kcdb_identity *) vid; if(FAILED(StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &namesize))) return KHM_ERROR_UNKNOWN; namesize += sizeof(wchar_t); if(!buffer || namesize > *pcbsize) { *pcbsize = namesize; return KHM_ERROR_TOO_LONG; } StringCbCopy(buffer, *pcbsize, id->name); *pcbsize = namesize; return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_default(khm_handle * pvid) { khm_handle def; if (pvid == NULL) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); def = kcdb_def_identity; if (def != NULL) kcdb_identity_hold(def); LeaveCriticalSection(&cs_ident); *pvid = def; if (def != NULL) return KHM_ERROR_SUCCESS; else return KHM_ERROR_NOT_FOUND; } static khm_int32 kcdbint_ident_set_default(khm_handle vid, khm_boolean invoke_identpro) { kcdb_identity * new_def; kcdb_identity * old_def; khm_int32 rv; if (vid != NULL && !kcdb_is_active_identity(vid)) return KHM_ERROR_INVALID_PARAM; new_def = (kcdb_identity *)vid; if (new_def != NULL && (new_def->flags & KCDB_IDENT_FLAG_DEFAULT)) return KHM_ERROR_SUCCESS; if ((new_def == NULL && kcdb_def_identity == NULL) || (new_def == kcdb_def_identity)) return KHM_ERROR_SUCCESS; /* first check with the identity provider if this operation is permitted. */ if (invoke_identpro) { rv = kcdb_identpro_set_default(vid); if(rv != KHM_ERROR_NO_PROVIDER && KHM_FAILED(rv)) return rv; } EnterCriticalSection(&cs_ident); old_def = kcdb_def_identity; kcdb_def_identity = new_def; if(old_def != new_def) { if(old_def) { old_def->flags &= ~KCDB_IDENT_FLAG_DEFAULT; kcdb_identity_release((khm_handle) old_def); } if(new_def) { new_def->flags |= KCDB_IDENT_FLAG_DEFAULT; kcdb_identity_hold((khm_handle) new_def); } LeaveCriticalSection(&cs_ident); if (invoke_identpro) kcdbint_ident_post_message(KCDB_OP_NEW_DEFAULT, new_def); } else { LeaveCriticalSection(&cs_ident); } return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_set_default(khm_handle vid) { return kcdbint_ident_set_default(vid, TRUE); } KHMEXP khm_int32 KHMAPI kcdb_identity_set_default_int(khm_handle vid) { return kcdbint_ident_set_default(vid, FALSE); } KHMEXP khm_int32 KHMAPI kcdb_identity_get_config(khm_handle vid, khm_int32 flags, khm_handle * result) { khm_handle hkcdb; khm_handle hidents = NULL; khm_handle hident = NULL; khm_int32 rv; kcdb_identity * id; if(kcdb_is_active_identity(vid)) { id = (kcdb_identity *) vid; } else { return KHM_ERROR_INVALID_PARAM; } hkcdb = kcdb_get_config(); if(hkcdb) { rv = khc_open_space(hkcdb, L"Identity", 0, &hidents); if(KHM_FAILED(rv)) goto _exit; rv = khc_open_space(hidents, id->name, flags | KCONF_FLAG_NOPARSENAME, &hident); if(KHM_FAILED(rv)) { EnterCriticalSection(&cs_ident); id->flags &= ~KCDB_IDENT_FLAG_CONFIG; LeaveCriticalSection(&cs_ident); goto _exit; } EnterCriticalSection(&cs_ident); id->flags |= KCDB_IDENT_FLAG_CONFIG; LeaveCriticalSection(&cs_ident); *result = hident; } else rv = KHM_ERROR_UNKNOWN; _exit: if(hidents) khc_close_space(hidents); if(hkcdb) khc_close_space(hkcdb); return rv; } /*! \note cs_ident must be available. */ void kcdbint_ident_post_message(khm_int32 op, kcdb_identity * id) { kcdb_identity_hold(id); kmq_post_message(KMSG_KCDB, KMSG_KCDB_IDENT, op, (void *) id); } /*! \note cs_ident must be available. */ KHMEXP khm_int32 KHMAPI kcdb_identity_hold(khm_handle vid) { kcdb_identity * id; EnterCriticalSection(&cs_ident); if(kcdb_is_active_identity(vid)) { id = vid; id->refcount++; } else { LeaveCriticalSection(&cs_ident); return KHM_ERROR_INVALID_PARAM; } LeaveCriticalSection(&cs_ident); return ERROR_SUCCESS; } /*! \note cs_ident must be available. */ KHMEXP khm_int32 KHMAPI kcdb_identity_release(khm_handle vid) { kcdb_identity * id; khm_int32 refcount; EnterCriticalSection(&cs_ident); if(kcdb_is_identity(vid)) { id = vid; refcount = --id->refcount; if(refcount == 0) { /* We only delete identities which do not have a configuration. */ if (id->refcount == 0 && !(id->flags & KCDB_IDENT_FLAG_CONFIG)) kcdb_identity_delete(vid); } } else { LeaveCriticalSection(&cs_ident); return KHM_ERROR_INVALID_PARAM; } LeaveCriticalSection(&cs_ident); return ERROR_SUCCESS; } struct kcdb_idref_result { kcdb_identity * ident; khm_int32 flags; khm_size count; }; static khm_int32 KHMAPI kcdbint_idref_proc(khm_handle cred, void * r) { khm_handle vid; struct kcdb_idref_result *result; khm_int32 flags; result = (struct kcdb_idref_result *) r; if (KHM_SUCCEEDED(kcdb_cred_get_identity(cred, &vid))) { if (result->ident == (kcdb_identity *) vid) { result->count++; kcdb_cred_get_flags(cred, &flags); if (flags & KCDB_CRED_FLAG_RENEWABLE) { result->flags |= KCDB_IDENT_FLAG_CRED_RENEW; if (flags & KCDB_CRED_FLAG_INITIAL) { result->flags |= KCDB_IDENT_FLAG_RENEWABLE; } } if (flags & KCDB_CRED_FLAG_EXPIRED) { result->flags |= KCDB_IDENT_FLAG_CRED_EXP; if (flags & KCDB_CRED_FLAG_INITIAL) { result->flags |= KCDB_IDENT_FLAG_EXPIRED; } } if (flags & KCDB_CRED_FLAG_INITIAL) { result->flags |= KCDB_IDENT_FLAG_VALID; } } kcdb_identity_release(vid); } return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kcdb_identity_refresh(khm_handle vid) { kcdb_identity * ident; khm_int32 code = KHM_ERROR_SUCCESS; struct kcdb_idref_result result; EnterCriticalSection(&cs_ident); if (!kcdb_is_active_identity(vid)) { code = KHM_ERROR_INVALID_PARAM; goto _exit; } ident = (kcdb_identity *) vid; result.ident = ident; result.flags = 0; result.count = 0; LeaveCriticalSection(&cs_ident); kcdb_credset_apply(NULL, kcdbint_idref_proc, &result); if (result.count == 0) result.flags |= KCDB_IDENT_FLAG_EMPTY; kcdb_identity_set_flags(vid, result.flags, KCDB_IDENT_FLAGMASK_RDWR & ~(KCDB_IDENT_FLAG_DEFAULT | KCDB_IDENT_FLAG_SEARCHABLE | KCDB_IDENT_FLAG_STICKY)); EnterCriticalSection(&cs_ident); ident->refresh_cycle = kcdb_ident_refresh_cycle; _exit: LeaveCriticalSection(&cs_ident); if (code == 0) code = kcdb_identpro_update(vid); return code; } KHMEXP khm_int32 KHMAPI kcdb_identity_refresh_all(void) { kcdb_identity * ident; kcdb_identity * next; khm_int32 code = KHM_ERROR_SUCCESS; int hit_count; EnterCriticalSection(&cs_ident); kcdb_ident_refresh_cycle++; /* The do-while loop is here to account for race conditions. We release cs_ident in the for loop, so we don't actually have a guarantee that we traversed the whole identity list at the end. We repeat until all the identities are uptodate. */ do { hit_count = 0; for (ident = kcdb_identities; ident != NULL; ident = next) { next = LNEXT(ident); if (!kcdb_is_active_identity(ident) || ident->refresh_cycle == kcdb_ident_refresh_cycle) continue; kcdb_identity_hold((khm_handle) ident); LeaveCriticalSection(&cs_ident); kcdb_identity_refresh((khm_handle) ident); EnterCriticalSection(&cs_ident); kcdb_identity_release((khm_handle) ident); hit_count++; } } while (hit_count > 0); LeaveCriticalSection(&cs_ident); return code; } /*****************************************/ /* Custom property functions */ KHMEXP khm_int32 KHMAPI kcdb_identity_set_attr(khm_handle vid, khm_int32 attr_id, void * buffer, khm_size cbbuf) { kcdb_identity * id = NULL; kcdb_attrib * attrib = NULL; kcdb_type * type = NULL; khm_size slot; khm_size cbdest; khm_int32 code = KHM_ERROR_SUCCESS; EnterCriticalSection(&cs_ident); if(!kcdb_is_active_identity(vid)) { LeaveCriticalSection(&cs_ident); return KHM_ERROR_INVALID_PARAM; } id = (kcdb_identity *) vid; if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS)) { kcdb_buf_new(&id->buf, KCDB_BUF_DEFAULT); id->flags |= KCDB_IDENT_FLAG_ATTRIBS; } if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { LeaveCriticalSection(&cs_ident); return KHM_ERROR_INVALID_PARAM; } #if 0 /* actually, even if an attribute is computed, we still allow those values to be set. This is because computing values is only for credentials. If a computed value is used as a property in any other object, it is treated as a regular value */ if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { LeaveCriticalSection(&cs_ident); kcdb_attrib_release_info(attrib); return KHM_ERROR_INVALID_OPERATION; } #endif if (buffer == NULL) { /* we are removing a value */ slot = kcdb_buf_slot_by_id(&id->buf, attr_id); if (slot != KCDB_BUF_INVALID_SLOT && kcdb_buf_exist(&id->buf, slot)) kcdb_buf_alloc(&id->buf, slot, attr_id, 0); code = KHM_ERROR_SUCCESS; goto _exit; } if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { LeaveCriticalSection(&cs_ident); kcdb_attrib_release_info(attrib); return KHM_ERROR_INVALID_PARAM; } if(!(type->isValid(buffer,cbbuf))) { code = KHM_ERROR_TYPE_MISMATCH; goto _exit; } if((type->dup(buffer, cbbuf, NULL, &cbdest)) != KHM_ERROR_TOO_LONG) { code = KHM_ERROR_INVALID_PARAM; goto _exit; } kcdb_buf_alloc(&id->buf, KCDB_BUF_APPEND, attr_id, cbdest); slot = kcdb_buf_slot_by_id(&id->buf, attr_id); if(slot == KCDB_BUF_INVALID_SLOT || !kcdb_buf_exist(&id->buf, slot)) { code = KHM_ERROR_NO_RESOURCES; goto _exit; } if(KHM_FAILED(code = type->dup(buffer, cbbuf, kcdb_buf_get(&id->buf, slot), &cbdest))) { kcdb_buf_alloc(&id->buf, slot, attr_id, 0); goto _exit; } kcdb_buf_set_value_flag(&id->buf, slot); _exit: LeaveCriticalSection(&cs_ident); if(attrib) kcdb_attrib_release_info(attrib); if(type) kcdb_type_release_info(type); return code; } KHMEXP khm_int32 KHMAPI kcdb_identity_set_attrib(khm_handle vid, wchar_t * attr_name, void * buffer, khm_size cbbuf) { khm_int32 attr_id = -1; if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) return KHM_ERROR_INVALID_PARAM; return kcdb_identity_set_attr( vid, attr_id, buffer, cbbuf); } KHMEXP khm_int32 KHMAPI kcdb_identity_get_attr(khm_handle vid, khm_int32 attr_id, khm_int32 * attr_type, void * buffer, khm_size * pcbbuf) { khm_int32 code = KHM_ERROR_SUCCESS; kcdb_identity * id = NULL; kcdb_attrib * attrib = NULL; kcdb_type * type = NULL; khm_size slot; if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { return KHM_ERROR_INVALID_PARAM; } if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { kcdb_attrib_release_info(attrib); return KHM_ERROR_UNKNOWN; } if(attr_type) *attr_type = attrib->type; EnterCriticalSection(&cs_ident); if(!kcdb_is_active_identity(vid)) { code = KHM_ERROR_INVALID_PARAM; goto _exit; } id = (kcdb_identity *) vid; if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) || (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT || !kcdb_buf_val_exist(&id->buf, slot)) { code = KHM_ERROR_NOT_FOUND; goto _exit; } if(!buffer && !pcbbuf) { /* in this case the caller is only trying to determine if the field contains data. If we get here, then the value exists. */ code = KHM_ERROR_SUCCESS; goto _exit; } #if 0 if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { /* we should never hit this case */ #ifdef DEBUG assert(FALSE); #else code = KHM_ERROR_INVALID_OPERATION; #endif } else { #endif code = type->dup( kcdb_buf_get(&id->buf, slot), kcdb_buf_size(&id->buf, slot), buffer, pcbbuf); #if 0 } #endif _exit: LeaveCriticalSection(&cs_ident); if(type) kcdb_type_release_info(type); if(attrib) kcdb_attrib_release_info(attrib); return code; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_attrib(khm_handle vid, wchar_t * attr_name, khm_int32 * attr_type, void * buffer, khm_size * pcbbuf) { khm_int32 attr_id = -1; if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) return KHM_ERROR_NOT_FOUND; return kcdb_identity_get_attr(vid, attr_id, attr_type, buffer, pcbbuf); } KHMEXP khm_int32 KHMAPI kcdb_identity_get_attr_string(khm_handle vid, khm_int32 attr_id, wchar_t * buffer, khm_size * pcbbuf, khm_int32 flags) { khm_int32 code = KHM_ERROR_SUCCESS; kcdb_identity * id = NULL; kcdb_attrib * attrib = NULL; kcdb_type * type = NULL; khm_size slot; if(KHM_FAILED(kcdb_attrib_get_info(attr_id, &attrib))) { return KHM_ERROR_INVALID_PARAM; } if(KHM_FAILED(kcdb_type_get_info(attrib->type, &type))) { kcdb_attrib_release_info(attrib); return KHM_ERROR_UNKNOWN; } EnterCriticalSection(&cs_ident); if(!kcdb_is_active_identity(vid)) { code = KHM_ERROR_INVALID_PARAM; goto _exit; } id = (kcdb_identity *) vid; if(!(id->flags & KCDB_IDENT_FLAG_ATTRIBS) || (slot = kcdb_buf_slot_by_id(&id->buf, attr_id)) == KCDB_BUF_INVALID_SLOT || !kcdb_buf_val_exist(&id->buf, slot)) { code = KHM_ERROR_NOT_FOUND; goto _exit; } if(!buffer && !pcbbuf) { /* in this case the caller is only trying to determine if the field contains data. If we get here, then the value exists */ code = KHM_ERROR_SUCCESS; goto _exit; } #if 0 if(attrib->flags & KCDB_ATTR_FLAG_COMPUTED) { #ifdef DEBUG assert(FALSE); #else code = KHM_ERROR_INVALID_OPERATION; #endif } else { #endif if(kcdb_buf_exist(&id->buf, slot)) { code = type->toString( kcdb_buf_get(&id->buf, slot), kcdb_buf_size(&id->buf, slot), buffer, pcbbuf, flags); } else code = KHM_ERROR_NOT_FOUND; #if 0 } #endif _exit: LeaveCriticalSection(&cs_ident); if(type) kcdb_type_release_info(type); if(attrib) kcdb_attrib_release_info(attrib); return code; } KHMEXP khm_int32 KHMAPI kcdb_identity_get_attrib_string( khm_handle vid, wchar_t * attr_name, wchar_t * buffer, khm_size * pcbbuf, khm_int32 flags) { khm_int32 attr_id = -1; if(KHM_FAILED(kcdb_attrib_get_id(attr_name, &attr_id))) return KHM_ERROR_NOT_FOUND; return kcdb_identity_get_attr_string( vid, attr_id, buffer, pcbbuf, flags); } /*****************************************/ /* Identity provider interface functions */ /* NOT called with cs_ident held */ KHMEXP khm_int32 KHMAPI kcdb_identpro_validate_name(const wchar_t * name) { kcdb_ident_name_xfer namex; khm_handle sub; khm_size cch; khm_int32 rv = KHM_ERROR_SUCCESS; /* we need to verify the length and the contents of the string before calling the identity provider */ if(FAILED(StringCchLength(name, KCDB_IDENT_MAXCCH_NAME, &cch))) return KHM_ERROR_TOO_LONG; /* We can't really make an assumption about the valid characters in an identity. So we let the identity provider decide */ #ifdef VALIDATE_IDENTIY_CHARACTERS if(wcsspn(name, KCDB_IDENT_VALID_CHARS) != cch) return KHM_ERROR_INVALID_NAME; #endif EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { ZeroMemory(&namex, sizeof(namex)); namex.name_src = name; namex.result = KHM_ERROR_NOT_IMPLEMENTED; kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_VALIDATE_NAME, 0, (void *) &namex); rv = namex.result; } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_validate_identity(khm_handle identity) { khm_int32 rv = KHM_ERROR_SUCCESS; khm_handle sub; if(!kcdb_is_active_identity(identity)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_VALIDATE_IDENTITY, 0, (void *) identity); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_canon_name(const wchar_t * name_in, wchar_t * name_out, khm_size * cb_name_out) { khm_handle sub; kcdb_ident_name_xfer namex; wchar_t name_tmp[KCDB_IDENT_MAXCCH_NAME]; khm_int32 rv = KHM_ERROR_SUCCESS; khm_size cch; if(cb_name_out == 0 || FAILED(StringCchLength(name_in, KCDB_IDENT_MAXCCH_NAME, &cch))) return KHM_ERROR_INVALID_NAME; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { ZeroMemory(&namex, sizeof(namex)); ZeroMemory(name_tmp, sizeof(name_tmp)); namex.name_src = name_in; namex.name_dest = name_tmp; namex.cb_name_dest = sizeof(name_tmp); namex.result = KHM_ERROR_NOT_IMPLEMENTED; rv = kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_CANON_NAME, 0, (void *) &namex); if(KHM_SUCCEEDED(namex.result)) { const wchar_t * name_result; khm_size cb; if(name_in[0] != 0 && name_tmp[0] == 0) name_result = name_tmp; else name_result = name_in; if(FAILED(StringCbLength(name_result, KCDB_IDENT_MAXCB_NAME, &cb))) rv = KHM_ERROR_UNKNOWN; else { cb += sizeof(wchar_t); if(name_out == 0 || *cb_name_out < cb) { rv = KHM_ERROR_TOO_LONG; *cb_name_out = cb; } else { StringCbCopy(name_out, *cb_name_out, name_result); *cb_name_out = cb; rv = KHM_ERROR_SUCCESS; } } } } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_compare_name(const wchar_t * name1, const wchar_t * name2) { khm_handle sub; kcdb_ident_name_xfer namex; khm_int32 rv = 0; /* Generally in kcdb_identpro_* functions we don't emulate any behavior if the provider is not available, but lacking a way to make this known, we emulate here */ rv = wcscmp(name1, name2); EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { ZeroMemory(&namex, sizeof(namex)); namex.name_src = name1; namex.name_alt = name2; namex.result = rv; kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_COMPARE_NAME, 0, (void *) &namex); rv = namex.result; } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_set_default(khm_handle identity) { khm_handle sub; khm_int32 rv = KHM_ERROR_SUCCESS; if((identity != NULL) && !kcdb_is_active_identity(identity)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_SET_DEFAULT, (identity != NULL), (void *) identity); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_set_searchable(khm_handle identity, khm_boolean searchable) { khm_handle sub; khm_int32 rv = KHM_ERROR_SUCCESS; if(!kcdb_is_active_identity(identity)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg( sub, KMSG_IDENT, KMSG_IDENT_SET_SEARCHABLE, searchable, (void *) identity); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_update(khm_handle identity) { khm_handle sub; khm_int32 rv = KHM_ERROR_SUCCESS; if(!kcdb_is_active_identity(identity)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg(sub, KMSG_IDENT, KMSG_IDENT_UPDATE, 0, (void *) identity); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_notify_create(khm_handle identity) { khm_handle sub; khm_int32 rv = KHM_ERROR_SUCCESS; if(!kcdb_is_active_identity(identity)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg( sub, KMSG_IDENT, KMSG_IDENT_NOTIFY_CREATE, 0, (void *) identity); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identpro_get_ui_cb(void * rock) { khm_handle sub; khm_int32 rv = KHM_ERROR_SUCCESS; EnterCriticalSection(&cs_ident); if(kcdb_ident_sub != NULL) { sub = kcdb_ident_sub; } else { sub = NULL; rv = KHM_ERROR_NO_PROVIDER; } LeaveCriticalSection(&cs_ident); if(sub != NULL) { rv = kmq_send_sub_msg( sub, KMSG_IDENT, KMSG_IDENT_GET_UI_CALLBACK, 0, rock); } return rv; } KHMEXP khm_int32 KHMAPI kcdb_identity_enum(khm_int32 and_flags, khm_int32 eq_flags, wchar_t * name_buf, khm_size * pcb_buf, khm_size * pn_idents) { kcdb_identity * id; khm_int32 rv = KHM_ERROR_SUCCESS; khm_size cb_req = 0; khm_size n_idents = 0; size_t cb_curr; size_t cch_curr; size_t cch_left; HRESULT hr; if ((name_buf == NULL && pcb_buf == NULL && pn_idents == NULL) || (name_buf != NULL && pcb_buf == NULL)) return KHM_ERROR_INVALID_PARAM; eq_flags &= and_flags; EnterCriticalSection(&cs_ident); if (!kcdb_checked_config) { khm_handle h_kcdb = NULL; khm_handle h_idents = NULL; khm_handle h_ident = NULL; kcdb_checked_config = TRUE; kcdb_checking_config = TRUE; h_kcdb = kcdb_get_config(); if (!h_kcdb) goto _config_check_cleanup; if(KHM_FAILED(khc_open_space(h_kcdb, L"Identity", 0, &h_idents))) goto _config_check_cleanup; while(KHM_SUCCEEDED(khc_enum_subspaces(h_idents, h_ident, &h_ident))) { wchar_t wname[KCDB_IDENT_MAXCCH_NAME]; khm_size cb; khm_handle t_id; cb = sizeof(wname); if (KHM_FAILED(khc_get_config_space_name(h_ident, wname, &cb))) continue; LeaveCriticalSection(&cs_ident); if (KHM_SUCCEEDED(kcdb_identity_create(wname, KCDB_IDENT_FLAG_CREATE, &t_id))) kcdb_identity_release(t_id); EnterCriticalSection(&cs_ident); } _config_check_cleanup: if (h_kcdb) khc_close_space(h_kcdb); if (h_idents) khc_close_space(h_idents); kcdb_checking_config = FALSE; } for ( id = kcdb_identities; id != NULL; id = LNEXT(id) ) { if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == KCDB_IDENT_FLAG_ACTIVE) && ((id->flags & and_flags) == eq_flags)) { n_idents ++; hr = StringCbLength(id->name, KCDB_IDENT_MAXCB_NAME, &cb_curr); #ifdef DEBUG assert(SUCCEEDED(hr)); #endif cb_req += cb_curr + sizeof(wchar_t); } } cb_req += sizeof(wchar_t); if (pn_idents != NULL) *pn_idents = n_idents; if (pcb_buf != NULL && (name_buf == NULL || *pcb_buf < cb_req)) { *pcb_buf = cb_req; rv = KHM_ERROR_TOO_LONG; } else if(name_buf != NULL) { cch_left = (*pcb_buf) / sizeof(wchar_t); for (id = kcdb_identities; id != NULL; id = LNEXT(id)) { if (((id->flags & KCDB_IDENT_FLAG_ACTIVE) == KCDB_IDENT_FLAG_ACTIVE) && ((id->flags & and_flags) == eq_flags)) { StringCchLength(id->name, KCDB_IDENT_MAXCCH_NAME, &cch_curr); cch_curr++; StringCchCopy(name_buf, cch_left, id->name); cch_left -= cch_curr; name_buf += cch_curr; } } *name_buf = L'\0'; *pcb_buf = cb_req; } LeaveCriticalSection(&cs_ident); return rv; }