/* -*- mode: c; indent-tabs-mode: nil -*- */ /* * plugins/kdb/hdb/kdb_marshal.c * * Copyright 2009 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #include "k5-int.h" #if HAVE_UNISTD_H #include #endif #include #include #include #include #include "kdb5.h" #include "kdb_hdb.h" void kh_free_Principal(krb5_context context, Principal *principal) { kh_db_context *kh = KH_DB_CONTEXT(context); if (principal != NULL) (*kh->heim_free_principal)(kh->hcontext, principal); } void kh_free_Event(krb5_context context, Event *event) { kh_free_Principal(context, event->principal); } void kh_free_HostAddresses(krb5_context context, HostAddresses *addrs) { kh_db_context *kh = KH_DB_CONTEXT(context); if (addrs != NULL) (*kh->heim_free_addresses)(kh->hcontext, addrs); } #if 0 static krb5_error_code kh_marshal_octet_string(krb5_context context, const krb5_data *in_data, heim_octet_string *out_data) { out_data->data = malloc(in_data->length); if (out_data->data == NULL) return ENOMEM; memcpy(out_data->data, in_data->data, in_data->length); out_data->length = in_data->length; return 0; } static krb5_error_code kh_unmarshal_octet_string_contents(krb5_context context, const heim_octet_string *in_data, krb5_data *out_data) { out_data->magic = KV5M_DATA; out_data->data = malloc(in_data->length); if (out_data->data == NULL) return ENOMEM; memcpy(out_data->data, in_data->data, in_data->length); out_data->length = in_data->length; return 0; } static krb5_error_code kh_unmarshal_octet_string(krb5_context context, heim_octet_string *in_data, krb5_data **out_data) { krb5_error_code code; *out_data = k5alloc(sizeof(krb5_data), &code); if (code != 0) return code; code = kh_unmarshal_octet_string_contents(context, in_data, *out_data); if (code != 0) { free(*out_data); *out_data = NULL; return code; } return 0; } #endif static krb5_error_code kh_marshal_general_string(krb5_context context, const krb5_data *in_data, heim_general_string *out_str) { *out_str = malloc(in_data->length + 1); if (*out_str == NULL) return ENOMEM; memcpy(*out_str, in_data->data, in_data->length); (*out_str)[in_data->length] = '\0'; return 0; } static krb5_error_code kh_unmarshal_general_string_contents(krb5_context context, const heim_general_string in_str, krb5_data *out_data) { out_data->magic = KV5M_DATA; out_data->length = strlen(in_str); out_data->data = malloc(out_data->length); if (out_data->data == NULL) return ENOMEM; memcpy(out_data->data, in_str, out_data->length); return 0; } #if 0 static krb5_error_code kh_unmarshal_general_string(krb5_context context, const heim_general_string in_str, krb5_data **out_data) { krb5_error_code code; *out_data = k5alloc(sizeof(krb5_data), &code); if (code != 0) return code; code = kh_unmarshal_general_string_contents(context, in_str, *out_data); if (code != 0) { free(*out_data); *out_data = NULL; return code; } return 0; } #endif krb5_error_code kh_marshal_Principal(krb5_context context, krb5_const_principal kprinc, Principal **out_hprinc) { krb5_error_code code; Principal *hprinc; int i; hprinc = k5alloc(sizeof(*hprinc), &code); if (code != 0) return code; hprinc->name.name_type = kprinc->type; hprinc->name.name_string.val = k5alloc(kprinc->length * sizeof(heim_general_string), &code); if (code != 0) { kh_free_Principal(context, hprinc); return code; } for (i = 0; i < kprinc->length; i++) { code = kh_marshal_general_string(context, &kprinc->data[i], &hprinc->name.name_string.val[i]); if (code != 0) { kh_free_Principal(context, hprinc); return code; } hprinc->name.name_string.len++; } code = kh_marshal_general_string(context, &kprinc->realm, &hprinc->realm); if (code != 0) { kh_free_Principal(context, hprinc); return code; } *out_hprinc = hprinc; return 0; } krb5_error_code kh_unmarshal_Principal(krb5_context context, const Principal *hprinc, krb5_principal *out_kprinc) { krb5_error_code code; krb5_principal kprinc; unsigned int i; kprinc = k5alloc(sizeof(*kprinc), &code); if (code != 0) return code; kprinc->magic = KV5M_PRINCIPAL; kprinc->type = hprinc->name.name_type; kprinc->data = k5alloc(hprinc->name.name_string.len * sizeof(krb5_data), &code); if (code != 0) { krb5_free_principal(context, kprinc); return code; } for (i = 0; i < hprinc->name.name_string.len; i++) { code = kh_unmarshal_general_string_contents(context, hprinc->name.name_string.val[i], &kprinc->data[i]); if (code != 0) { krb5_free_principal(context, kprinc); return code; } kprinc->length++; } code = kh_unmarshal_general_string_contents(context, hprinc->realm, &kprinc->realm); if (code != 0) { krb5_free_principal(context, kprinc); return code; } *out_kprinc = kprinc; return 0; } static krb5_error_code kh_marshal_Event(krb5_context context, const krb5_db_entry *kentry, Event *event) { krb5_error_code code; krb5_timestamp mod_time = 0; krb5_principal mod_princ = NULL; memset(event, 0, sizeof(*event)); code = krb5_dbe_lookup_mod_princ_data(context, (krb5_db_entry *)kentry, &mod_time, &mod_princ); if (code != 0) return code; event->time = mod_time; if (mod_princ != NULL) { code = kh_marshal_Principal(context, mod_princ, &event->principal); if (code != 0) { krb5_free_principal(context, mod_princ); return code; } } krb5_free_principal(context, mod_princ); return 0; } static krb5_error_code kh_unmarshal_Event(krb5_context context, const Event *event, krb5_db_entry *kentry) { krb5_error_code code; krb5_principal princ = NULL; if (event->principal != NULL) { code = kh_unmarshal_Principal(context, event->principal, &princ); if (code != 0) return code; } code = krb5_dbe_update_mod_princ_data(context, kentry, event->time, princ); krb5_free_principal(context, princ); return code; } static krb5_error_code kh_marshal_HDBFlags(krb5_context context, krb5_flags kflags, HDBFlags *hflags) { memset(hflags, 0, sizeof(*hflags)); if (kflags & KRB5_KDB_DISALLOW_TGT_BASED) hflags->initial = 1; if ((kflags & KRB5_KDB_DISALLOW_FORWARDABLE) == 0) hflags->forwardable = 1; if ((kflags & KRB5_KDB_DISALLOW_PROXIABLE) == 0) hflags->proxiable = 1; if ((kflags & KRB5_KDB_DISALLOW_RENEWABLE) == 0) hflags->renewable = 1; if ((kflags & KRB5_KDB_DISALLOW_POSTDATED) == 0) hflags->postdate = 1; if ((kflags & KRB5_KDB_DISALLOW_SVR) == 0) hflags->server = 1; hflags->client = 1; if (kflags & KRB5_KDB_DISALLOW_ALL_TIX) hflags->invalid = 1; if (kflags & KRB5_KDB_REQUIRES_PRE_AUTH) hflags->require_preauth = 1; if (kflags & KRB5_KDB_PWCHANGE_SERVICE) hflags->change_pw = 1; if (kflags & KRB5_KDB_REQUIRES_HW_AUTH) hflags->require_hwauth = 1; if (kflags & KRB5_KDB_OK_AS_DELEGATE) hflags->ok_as_delegate = 1; /* hflags->user_to_user */ /* hflags->immutable */ if (kflags & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE) hflags->trusted_for_delegation = 1; /* hflags->allow_kerberos4 */ /* hflags->allow_digest */ return 0; } static krb5_error_code kh_unmarshal_HDBFlags(krb5_context context, HDBFlags hflags, krb5_flags *kflags) { *kflags = 0; if (hflags.initial) *kflags |= KRB5_KDB_DISALLOW_TGT_BASED; if (!hflags.forwardable) *kflags |= KRB5_KDB_DISALLOW_FORWARDABLE; if (!hflags.proxiable) *kflags |= KRB5_KDB_DISALLOW_PROXIABLE; if (!hflags.renewable) *kflags |= KRB5_KDB_DISALLOW_RENEWABLE; if (!hflags.postdate) *kflags |= KRB5_KDB_DISALLOW_POSTDATED; if (!hflags.server) *kflags |= KRB5_KDB_DISALLOW_SVR; if (hflags.client) ; if (hflags.invalid) *kflags |= KRB5_KDB_DISALLOW_ALL_TIX; if (hflags.require_preauth) *kflags |= KRB5_KDB_REQUIRES_PRE_AUTH; if (hflags.change_pw) *kflags |= KRB5_KDB_PWCHANGE_SERVICE; if (hflags.require_hwauth) *kflags |= KRB5_KDB_REQUIRES_HW_AUTH; if (hflags.ok_as_delegate) *kflags |= KRB5_KDB_OK_AS_DELEGATE; if (hflags.user_to_user) ; if (hflags.immutable) ; if (hflags.trusted_for_delegation) *kflags |= KRB5_KDB_OK_TO_AUTH_AS_DELEGATE; if (hflags.allow_kerberos4) ; if (hflags.allow_digest) ; return 0; } static krb5_error_code kh_marshal_Key(krb5_context context, const krb5_key_data *kkey, Key *hkey) { krb5_error_code code; memset(hkey, 0, sizeof(*hkey)); hkey->key.keytype = kkey->key_data_type[0]; hkey->key.keyvalue.data = k5alloc(kkey->key_data_length[0], &code); if (code != 0) return code; memcpy(hkey->key.keyvalue.data, kkey->key_data_contents[0], kkey->key_data_length[0]); hkey->key.keyvalue.length = kkey->key_data_length[0]; if (kkey->key_data_contents[1] != NULL) { Salt *salt; salt = k5alloc(sizeof(*salt), &code); if (code != 0) goto cleanup; switch (kkey->key_data_type[1]) { case KRB5_KDB_SALTTYPE_NORMAL: salt->type = hdb_pw_salt; break; case KRB5_KDB_SALTTYPE_AFS3: salt->type = hdb_afs3_salt; break; default: salt->type = 0; break; } salt->salt.data = k5alloc(kkey->key_data_length[1], &code); if (code != 0) { free(salt); goto cleanup; } memcpy(salt->salt.data, kkey->key_data_contents[1], kkey->key_data_length[1]); salt->salt.length = kkey->key_data_length[1]; hkey->salt = salt; } cleanup: if (code != 0 && hkey->key.keyvalue.data != NULL) free(hkey->key.keyvalue.data); return code; } static krb5_error_code kh_unmarshal_Key(krb5_context context, const hdb_entry *hentry, const Key *hkey, krb5_key_data *kkey) { memset(kkey, 0, sizeof(*kkey)); kkey->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY; kkey->key_data_kvno = hentry->kvno; kkey->key_data_type[0] = hkey->key.keytype; kkey->key_data_contents[0] = malloc(hkey->key.keyvalue.length); if (kkey->key_data_contents[0] == NULL) return ENOMEM; memcpy(kkey->key_data_contents[0], hkey->key.keyvalue.data, hkey->key.keyvalue.length); kkey->key_data_length[0] = hkey->key.keyvalue.length; if (hkey->salt != NULL) { switch (hkey->salt->type) { case hdb_pw_salt: kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL; break; case hdb_afs3_salt: kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_AFS3; break; default: kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL; break; } kkey->key_data_contents[1] = malloc(hkey->salt->salt.length); if (kkey->key_data_contents[1] == NULL) { memset(kkey->key_data_contents[0], 0, kkey->key_data_length[0]); free(kkey->key_data_contents[0]); return ENOMEM; } memcpy(kkey->key_data_contents[1], hkey->salt->salt.data, hkey->salt->salt.length); kkey->key_data_length[1] = hkey->salt->salt.length; } return 0; } /* * Extension marshalers */ static krb5_error_code kh_marshal_HDB_extension_data_last_pw_change(krb5_context context, const krb5_db_entry *kentry, HDB_extension *hext) { krb5_timestamp stamp; krb5_error_code code; code = krb5_dbe_lookup_last_pwd_change(context, (krb5_db_entry *)kentry, &stamp); if (code != 0) return code; hext->data.u.last_pw_change = stamp; return 0; } static krb5_error_code kh_unmarshal_HDB_extension_data_last_pw_change(krb5_context context, HDB_extension *hext, krb5_db_entry *kentry) { return krb5_dbe_update_last_pwd_change(context, kentry, hext->data.u.last_pw_change); } typedef krb5_error_code (*kh_hdb_marshal_extension_fn)(krb5_context, const krb5_db_entry *, HDB_extension *); typedef krb5_error_code (*kh_hdb_unmarshal_extension_fn)(krb5_context, HDB_extension *, krb5_db_entry *); struct { kh_hdb_marshal_extension_fn marshal; kh_hdb_unmarshal_extension_fn unmarshal; } kh_hdb_extension_vtable[] = { { NULL, NULL }, /* choice_HDB_extension_data_asn1_ellipsis */ { NULL, NULL }, /* choice_HDB_extension_data_pkinit_acl */ { NULL, NULL }, /* choice_HDB_extension_data_pkinit_cert_hash */ { NULL, NULL }, /* choice_HDB_extension_data_allowed_to_delegate_to */ { NULL, NULL }, /* choice_HDB_extension_data_lm_owf */ { NULL, NULL }, /* choice_HDB_extension_data_password */ { NULL, NULL }, /* choice_HDB_extension_data_aliases */ { kh_marshal_HDB_extension_data_last_pw_change, kh_unmarshal_HDB_extension_data_last_pw_change } }; static const size_t kh_hdb_extension_count = sizeof(kh_hdb_extension_vtable) / sizeof(kh_hdb_extension_vtable[0]); static krb5_error_code kh_marshal_HDB_extension(krb5_context context, const krb5_db_entry *kentry, HDB_extension *hext) { kh_hdb_marshal_extension_fn marshal = NULL; if (hext->data.element < kh_hdb_extension_count) marshal = kh_hdb_extension_vtable[hext->data.element].marshal; if (marshal == NULL) return KRB5_KDB_DBTYPE_NOSUP; return (*marshal)(context, kentry, hext); } static krb5_error_code kh_unmarshal_HDB_extension(krb5_context context, HDB_extension *hext, krb5_db_entry *kentry) { kh_hdb_unmarshal_extension_fn unmarshal = NULL; if (hext->data.element < kh_hdb_extension_count) unmarshal = kh_hdb_extension_vtable[hext->data.element].unmarshal; if (unmarshal == NULL) return hext->mandatory ? KRB5_KDB_DBTYPE_NOSUP : 0; return (*unmarshal)(context, hext, kentry); } static krb5_error_code kh_marshal_HDB_extensions(krb5_context context, const krb5_db_entry *kentry, HDB_extensions *hexts) { unsigned int i; krb5_error_code code; hexts->val = k5alloc(kh_hdb_extension_count * sizeof(HDB_extension), &code); if (code != 0) return code; hexts->len = 0; for (i = 0; i < kh_hdb_extension_count; i++) { HDB_extension *hext = &hexts->val[hexts->len]; hext->data.element = i; code = kh_marshal_HDB_extension(context, kentry, hext); if (code == KRB5_KDB_DBTYPE_NOSUP) continue; else if (code != 0) break; hexts->len++; } return code; } static krb5_error_code kh_unmarshal_HDB_extensions(krb5_context context, HDB_extensions *hexts, krb5_db_entry *kentry) { unsigned int i; krb5_error_code code = 0; for (i = 0; i < hexts->len; i++) { code = kh_unmarshal_HDB_extension(context, &hexts->val[i], kentry); if (code != 0) break; } return code; } krb5_error_code kh_marshal_hdb_entry(krb5_context context, const krb5_db_entry *kentry, hdb_entry *hentry) { kh_db_context *kh = KH_DB_CONTEXT(context); krb5_error_code code; krb5_int16 kvno = 0; int i; memset(hentry, 0, sizeof(*hentry)); code = kh_marshal_Principal(context, kentry->princ, &hentry->principal); if (code != 0) goto cleanup; code = kh_marshal_HDBFlags(context, kentry->attributes, &hentry->flags); if (code != 0) goto cleanup; if (kentry->expiration) { hentry->valid_end = k5alloc(sizeof(KerberosTime), &code); if (code != 0) goto cleanup; *(hentry->valid_end) = kentry->expiration; } if (kentry->pw_expiration) { hentry->pw_end = k5alloc(sizeof(KerberosTime), &code); if (code != 0) goto cleanup; *(hentry->pw_end) = kentry->pw_expiration; } if (kentry->max_life) { hentry->max_life = k5alloc(sizeof(unsigned int), &code); if (code != 0) goto cleanup; *(hentry->max_life) = kentry->max_life; } if (kentry->max_renewable_life) { hentry->max_renew = k5alloc(sizeof(unsigned int), &code); if (code != 0) goto cleanup; *(hentry->max_renew) = kentry->max_renewable_life; } /* last_success */ /* last_failed */ /* fail_auth_count */ /* n_tl_data */ if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0) { hentry->modified_by = k5alloc(sizeof(Event), &code); if (code != 0) goto cleanup; code = kh_marshal_Event(context, kentry, hentry->modified_by); } else { code = kh_marshal_Event(context, kentry, &hentry->created_by); } if (code != 0) goto cleanup; hentry->extensions = k5alloc(sizeof(HDB_extensions), &code); if (code != 0) goto cleanup; code = kh_marshal_HDB_extensions(context, kentry, hentry->extensions); if (code != 0) goto cleanup; hentry->keys.len = 0; hentry->keys.val = k5alloc(kentry->n_key_data * sizeof(Key), &code); if (code != 0) goto cleanup; for (i = 0; i < kentry->n_key_data; i++) { code = kh_marshal_Key(context, &kentry->key_data[i], &hentry->keys.val[hentry->keys.len]); if (code != 0) goto cleanup; if (kentry->key_data[i].key_data_kvno > kvno) kvno = kentry->key_data[i].key_data_kvno; hentry->keys.len++; } hentry->kvno = kvno; cleanup: if (code != 0) { hdb_entry_ex hext; hext.ctx = NULL; hext.entry = *hentry; hext.free_entry = NULL; kh_hdb_free_entry(context, kh, &hext); memset(hentry, 0, sizeof(*hentry)); } return code; } krb5_error_code kh_unmarshal_hdb_entry(krb5_context context, const hdb_entry *hentry, krb5_db_entry *kentry) { kh_db_context *kh = KH_DB_CONTEXT(context); krb5_error_code code; unsigned int i; memset(kentry, 0, sizeof(*kentry)); kentry->magic = KRB5_KDB_MAGIC_NUMBER; kentry->len = KRB5_KDB_V1_BASE_LENGTH; code = kh_unmarshal_Principal(context, hentry->principal, &kentry->princ); if (code != 0) goto cleanup; code = kh_unmarshal_HDBFlags(context, hentry->flags, &kentry->attributes); if (code != 0) goto cleanup; if (hentry->max_life != NULL) kentry->max_life = *(hentry->max_life); if (hentry->max_renew != NULL) kentry->max_renewable_life = *(hentry->max_renew); if (hentry->valid_end != NULL) kentry->expiration = *(hentry->valid_end); if (hentry->pw_end != NULL) kentry->pw_expiration = *(hentry->pw_end); /* last_success */ /* last_failed */ /* fail_auth_count */ /* n_tl_data */ code = kh_unmarshal_Event(context, hentry->modified_by ? hentry->modified_by : &hentry->created_by, kentry); if (code != 0) goto cleanup; code = kh_unmarshal_HDB_extensions(context, hentry->extensions, kentry); if (code != 0) goto cleanup; kentry->key_data = k5alloc(hentry->keys.len * sizeof(krb5_key_data), &code); if (code != 0) goto cleanup; for (i = 0; i < hentry->keys.len; i++) { code = kh_unmarshal_Key(context, hentry, &hentry->keys.val[i], &kentry->key_data[i]); if (code != 0) goto cleanup; kentry->n_key_data++; } cleanup: if (code != 0) kh_kdb_free_entry(context, kh, kentry); return code; }