From ee936431c88a7b089c9c2780d63b393813d114e5 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 13 Mar 2012 10:29:00 +0100 Subject: Move some krb5 keys related functions from ipa-client to util --- ipa-client/ipa-getkeytab.c | 381 +-------------------------------------------- util/ipa_krb5.c | 362 ++++++++++++++++++++++++++++++++++++++++++ util/ipa_krb5.h | 36 +++++ 3 files changed, 403 insertions(+), 376 deletions(-) diff --git a/ipa-client/ipa-getkeytab.c b/ipa-client/ipa-getkeytab.c index 7d7b97176..ca6e63413 100644 --- a/ipa-client/ipa-getkeytab.c +++ b/ipa-client/ipa-getkeytab.c @@ -41,30 +41,6 @@ #include "ipa_krb5.h" #include "ipa-client-common.h" -/* Salt types */ -#define NO_SALT -1 -#define KRB5_KDB_SALTTYPE_NORMAL 0 -#define KRB5_KDB_SALTTYPE_V4 1 -#define KRB5_KDB_SALTTYPE_NOREALM 2 -#define KRB5_KDB_SALTTYPE_ONLYREALM 3 -#define KRB5_KDB_SALTTYPE_SPECIAL 4 -#define KRB5_KDB_SALTTYPE_AFS3 5 - -#define KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1" -#define KEYTAB_RET_OID "2.16.840.1.113730.3.8.10.2" - -struct krb_key_salt { - krb5_enctype enctype; - krb5_int32 salttype; - krb5_keyblock key; - krb5_data salt; -}; - -struct keys_container { - krb5_int32 nkeys; - struct krb_key_salt *ksdata; -}; - static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit) { sasl_interact_t *in = NULL; @@ -122,357 +98,6 @@ static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *s return ret; } -static void free_keys_contents(krb5_context krbctx, struct keys_container *keys) -{ - struct krb_key_salt *ksdata; - int i; - - ksdata = keys->ksdata; - for (i = 0; i < keys->nkeys; i++) { - krb5_free_keyblock_contents(krbctx, &ksdata[i].key); - krb5_free_data_contents(krbctx, &ksdata[i].salt); - } - free(ksdata); - - keys->ksdata = NULL; - keys->nkeys = 0; -} - -/* Determines Encryption and Salt types, - * allocates key_salt data storage, - * filters out equivalent encodings, - * returns 0 if no enctypes available, >0 if enctypes are available */ -static int prep_ksdata(krb5_context krbctx, const char *str, - struct keys_container *keys) -{ - struct krb_key_salt *ksdata; - krb5_error_code krberr; - int n, i, j, nkeys; - - if (str == NULL) { - krb5_enctype *ktypes; - - krberr = krb5_get_permitted_enctypes(krbctx, &ktypes); - if (krberr) { - fprintf(stderr, _("No system preferred enctypes ?!\n")); - return 0; - } - - for (n = 0; ktypes[n]; n++) /* count */ ; - - ksdata = calloc(n + 1, sizeof(struct krb_key_salt)); - if (NULL == ksdata) { - fprintf(stderr, _("Out of memory!?\n")); - return 0; - } - - for (i = 0; i < n; i++) { - ksdata[i].enctype = ktypes[i]; - ksdata[i].salttype = KRB5_KDB_SALTTYPE_NORMAL; - } - - ipa_krb5_free_ktypes(krbctx, ktypes); - - nkeys = i; - - } else { - char *tmp, *t, *p, *q; - - t = tmp = strdup(str); - if (!tmp) { - fprintf(stderr, _("Out of memory\n")); - return 0; - } - - /* count */ - n = 0; - while ((p = strchr(t, ','))) { - t = p+1; - n++; - } - n++; /* count the last one that is 0 terminated instead */ - - /* at the end we will have at most n entries + 1 terminating */ - ksdata = calloc(n + 1, sizeof(struct krb_key_salt)); - if (!ksdata) { - fprintf(stderr, _("Out of memory\n")); - return 0; - } - - for (i = 0, j = 0, t = tmp; i < n; i++) { - - p = strchr(t, ','); - if (p) *p = '\0'; - - q = strchr(t, ':'); - if (q) *q++ = '\0'; - - krberr = krb5_string_to_enctype(t, &ksdata[j].enctype); - if (krberr != 0) { - fprintf(stderr, - _("Warning unrecognized encryption type: [%s]\n"), t); - if (p) t = p + 1; - continue; - } - if (p) t = p + 1; - - if (!q) { - ksdata[j].salttype = KRB5_KDB_SALTTYPE_NORMAL; - j++; - continue; - } - - krberr = krb5_string_to_salttype(q, &ksdata[j].salttype); - if (krberr != 0) { - fprintf(stderr, - _("Warning unrecognized salt type: [%s]\n"), q); - continue; - } - - j++; - } - - nkeys = j; - - free(tmp); - } - - /* Check we don't already have a key with a similar encoding, - * it would just produce redundant data and this is what the - * MIT code do anyway */ - - for (i = 0, n = 0; i < nkeys; i++ ) { - krb5_boolean similar = 0; - - for (j = 0; j < i; j++) { - krberr = krb5_c_enctype_compare(krbctx, - ksdata[j].enctype, - ksdata[i].enctype, - &similar); - if (krberr) { - free_keys_contents(krbctx, keys); - free(ksdata); - fprintf(stderr, _("Enctype comparison failed!\n")); - return 0; - } - if (similar && - (ksdata[j].salttype == ksdata[i].salttype)) { - break; - } - } - if (j < i) { - /* redundant encoding, remove it, and shift others */ - int x; - for (x = i; x < nkeys-1; x++) { - ksdata[x].enctype = ksdata[x+1].enctype; - ksdata[x].salttype = ksdata[x+1].salttype; - } - continue; - } - /* count only confirmed enc/salt tuples */ - n++; - } - - keys->nkeys = n; - keys->ksdata = ksdata; - - return n; -} - -static int create_keys(krb5_context krbctx, - krb5_principal princ, - char *password, - const char *enctypes_string, - struct keys_container *keys) -{ - struct krb_key_salt *ksdata; - krb5_error_code krberr; - krb5_data key_password; - krb5_data *realm = NULL; - int i, nkeys; - int ret; - - ret = prep_ksdata(krbctx, enctypes_string, keys); - if (ret == 0) return 0; - - ksdata = keys->ksdata; - nkeys = keys->nkeys; - - if (password) { - key_password.data = password; - key_password.length = strlen(password); - - realm = krb5_princ_realm(krbctx, princ); - } - - for (i = 0; i < nkeys; i++) { - krb5_data *salt; - - if (!password) { - /* cool, random keys */ - krberr = krb5_c_make_random_key(krbctx, - ksdata[i].enctype, - &ksdata[i].key); - if (krberr) { - fprintf(stderr, _("Failed to create random key!\n")); - return 0; - } - /* set the salt to NO_SALT as the key was random */ - ksdata[i].salttype = NO_SALT; - continue; - } - - /* Make keys using password and required salt */ - switch (ksdata[i].salttype) { - case KRB5_KDB_SALTTYPE_ONLYREALM: - krberr = krb5_copy_data(krbctx, realm, &salt); - if (krberr) { - fprintf(stderr, _("Failed to create key!\n")); - return 0; - } - - ksdata[i].salt.length = salt->length; - ksdata[i].salt.data = malloc(salt->length); - if (!ksdata[i].salt.data) { - fprintf(stderr, _("Out of memory!\n")); - return 0; - } - memcpy(ksdata[i].salt.data, salt->data, salt->length); - krb5_free_data(krbctx, salt); - break; - - case KRB5_KDB_SALTTYPE_NOREALM: - krberr = ipa_krb5_principal2salt_norealm(krbctx, princ, &ksdata[i].salt); - if (krberr) { - fprintf(stderr, _("Failed to create key!\n")); - return 0; - } - break; - - case KRB5_KDB_SALTTYPE_NORMAL: - krberr = krb5_principal2salt(krbctx, princ, &ksdata[i].salt); - if (krberr) { - fprintf(stderr, _("Failed to create key!\n")); - return 0; - } - break; - - /* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */ - - case KRB5_KDB_SALTTYPE_AFS3: - /* Comment from MIT sources: - * * Why do we do this? Well, the afs_mit_string_to_key - * * needs to use strlen, and the realm is not NULL - * * terminated.... - */ - ksdata[i].salt.data = (char *)malloc(realm->length + 1); - if (NULL == ksdata[i].salt.data) { - fprintf(stderr, _("Out of memory!\n")); - return 0; - } - memcpy((char *)ksdata[i].salt.data, - (char *)realm->data, realm->length); - ksdata[i].salt.data[realm->length] = '\0'; - /* AFS uses a special length (UGLY) */ - ksdata[i].salt.length = SALT_TYPE_AFS_LENGTH; - break; - - default: - fprintf(stderr, _("Bad or unsupported salt type (%d)!\n"), - ksdata[i].salttype); - return 0; - } - - krberr = krb5_c_string_to_key(krbctx, - ksdata[i].enctype, - &key_password, - &ksdata[i].salt, - &ksdata[i].key); - if (krberr) { - fprintf(stderr, _("Failed to create key!\n")); - return 0; - } - - /* set back salt length to real value if AFS3 */ - if (ksdata[i].salttype == KRB5_KDB_SALTTYPE_AFS3) { - ksdata[i].salt.length = realm->length; - } - } - - return nkeys; -} - -static struct berval *create_key_control(struct keys_container *keys, - const char *principalName) -{ - struct krb_key_salt *ksdata; - struct berval *bval; - BerElement *be; - int ret, i; - - be = ber_alloc_t(LBER_USE_DER); - if (!be) { - return NULL; - } - - ret = ber_printf(be, "{s{", principalName); - if (ret == -1) { - ber_free(be, 1); - return NULL; - } - - ksdata = keys->ksdata; - for (i = 0; i < keys->nkeys; i++) { - - /* we set only the EncryptionKey and salt, no s2kparams */ - - ret = ber_printf(be, "{t[{t[i]t[o]}]", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - (ber_int_t)ksdata[i].enctype, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - (char *)ksdata[i].key.contents, (ber_len_t)ksdata[i].key.length); - - if (ret == -1) { - ber_free(be, 1); - return NULL; - } - - if (ksdata[i].salttype == NO_SALT) { - ret = ber_printf(be, "}"); - continue; - } - - /* we have to pass a salt structure */ - ret = ber_printf(be, "t[{t[i]t[o]}]}", - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), - (ber_int_t)ksdata[i].salttype, - (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), - (char *)ksdata[i].salt.data, (ber_len_t)ksdata[i].salt.length); - - if (ret == -1) { - ber_free(be, 1); - return NULL; - } - } - - ret = ber_printf(be, "}}"); - if (ret == -1) { - ber_free(be, 1); - return NULL; - } - - ret = ber_flatten(be, &bval); - if (ret == -1) { - ber_free(be, 1); - return NULL; - } - - ber_free(be, 1); - return bval; -} - int filter_keys(krb5_context krbctx, struct keys_container *keys, ber_int_t *enctypes) { @@ -854,6 +479,7 @@ int main(int argc, const char *argv[]) krb5_keytab kt; int kvno; int i, ret; + char *err_msg; ret = init_gettext(); if (ret) { @@ -955,8 +581,11 @@ int main(int argc, const char *argv[]) } /* create key material */ - ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys); + ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys, &err_msg); if (!ret) { + if (err_msg != NULL) { + fprintf(stderr, "%s", err_msg); + } fprintf(stderr, _("Failed to create key material\n")); exit(8); } diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c index 0e82b2380..0240c079e 100644 --- a/util/ipa_krb5.c +++ b/util/ipa_krb5.c @@ -26,6 +26,9 @@ #include #include +#include +#define _(STRING) gettext(STRING) + #include "ipa_krb5.h" /* Salt types */ @@ -530,3 +533,362 @@ krb5_error_code filter_key_salt_tuples(krb5_context context, return 0; } +struct berval *create_key_control(struct keys_container *keys, + const char *principalName) +{ + struct krb_key_salt *ksdata; + struct berval *bval; + BerElement *be; + int ret, i; + + be = ber_alloc_t(LBER_USE_DER); + if (!be) { + return NULL; + } + + ret = ber_printf(be, "{s{", principalName); + if (ret == -1) { + ber_free(be, 1); + return NULL; + } + + ksdata = keys->ksdata; + for (i = 0; i < keys->nkeys; i++) { + + /* we set only the EncryptionKey and salt, no s2kparams */ + + ret = ber_printf(be, "{t[{t[i]t[o]}]", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + (ber_int_t)ksdata[i].enctype, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + (char *)ksdata[i].key.contents, (ber_len_t)ksdata[i].key.length); + + if (ret == -1) { + ber_free(be, 1); + return NULL; + } + + if (ksdata[i].salttype == NO_SALT) { + ret = ber_printf(be, "}"); + continue; + } + + /* we have to pass a salt structure */ + ret = ber_printf(be, "t[{t[i]t[o]}]}", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + (ber_int_t)ksdata[i].salttype, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + (char *)ksdata[i].salt.data, (ber_len_t)ksdata[i].salt.length); + + if (ret == -1) { + ber_free(be, 1); + return NULL; + } + } + + ret = ber_printf(be, "}}"); + if (ret == -1) { + ber_free(be, 1); + return NULL; + } + + ret = ber_flatten(be, &bval); + if (ret == -1) { + ber_free(be, 1); + return NULL; + } + + ber_free(be, 1); + return bval; +} + +void free_keys_contents(krb5_context krbctx, struct keys_container *keys) +{ + struct krb_key_salt *ksdata; + int i; + + ksdata = keys->ksdata; + for (i = 0; i < keys->nkeys; i++) { + krb5_free_keyblock_contents(krbctx, &ksdata[i].key); + krb5_free_data_contents(krbctx, &ksdata[i].salt); + } + free(ksdata); + + keys->ksdata = NULL; + keys->nkeys = 0; +} + +/* Determines Encryption and Salt types, + * allocates key_salt data storage, + * filters out equivalent encodings, + * returns 0 if no enctypes available, >0 if enctypes are available */ +static int prep_ksdata(krb5_context krbctx, const char *str, + struct keys_container *keys, + char **err_msg) +{ + struct krb_key_salt *ksdata; + krb5_error_code krberr; + int n, i, j, nkeys; + + *err_msg = NULL; + + if (str == NULL) { + krb5_enctype *ktypes; + + krberr = krb5_get_permitted_enctypes(krbctx, &ktypes); + if (krberr) { + *err_msg = _("No system preferred enctypes ?!\n"); + return 0; + } + + for (n = 0; ktypes[n]; n++) /* count */ ; + + ksdata = calloc(n + 1, sizeof(struct krb_key_salt)); + if (NULL == ksdata) { + *err_msg = _("Out of memory!?\n"); + return 0; + } + + for (i = 0; i < n; i++) { + ksdata[i].enctype = ktypes[i]; + ksdata[i].salttype = KRB5_KDB_SALTTYPE_NORMAL; + } + + ipa_krb5_free_ktypes(krbctx, ktypes); + + nkeys = i; + + } else { + char *tmp, *t, *p, *q; + + t = tmp = strdup(str); + if (!tmp) { + *err_msg = _("Out of memory\n"); + return 0; + } + + /* count */ + n = 0; + while ((p = strchr(t, ','))) { + t = p+1; + n++; + } + n++; /* count the last one that is 0 terminated instead */ + + /* at the end we will have at most n entries + 1 terminating */ + ksdata = calloc(n + 1, sizeof(struct krb_key_salt)); + if (!ksdata) { + *err_msg = _("Out of memory\n"); + return 0; + } + + for (i = 0, j = 0, t = tmp; i < n; i++) { + + p = strchr(t, ','); + if (p) *p = '\0'; + + q = strchr(t, ':'); + if (q) *q++ = '\0'; + + krberr = krb5_string_to_enctype(t, &ksdata[j].enctype); + if (krberr != 0) { + *err_msg = _("Warning unrecognized encryption type.\n"); + if (p) t = p + 1; + continue; + } + if (p) t = p + 1; + + if (!q) { + ksdata[j].salttype = KRB5_KDB_SALTTYPE_NORMAL; + j++; + continue; + } + + krberr = krb5_string_to_salttype(q, &ksdata[j].salttype); + if (krberr != 0) { + *err_msg = _("Warning unrecognized salt type.\n"); + continue; + } + + j++; + } + + nkeys = j; + + free(tmp); + } + + /* Check we don't already have a key with a similar encoding, + * it would just produce redundant data and this is what the + * MIT code do anyway */ + + for (i = 0, n = 0; i < nkeys; i++ ) { + krb5_boolean similar = 0; + + for (j = 0; j < i; j++) { + krberr = krb5_c_enctype_compare(krbctx, + ksdata[j].enctype, + ksdata[i].enctype, + &similar); + if (krberr) { + free_keys_contents(krbctx, keys); + free(ksdata); + *err_msg = _("Enctype comparison failed!\n"); + return 0; + } + if (similar && + (ksdata[j].salttype == ksdata[i].salttype)) { + break; + } + } + if (j < i) { + /* redundant encoding, remove it, and shift others */ + int x; + for (x = i; x < nkeys-1; x++) { + ksdata[x].enctype = ksdata[x+1].enctype; + ksdata[x].salttype = ksdata[x+1].salttype; + } + continue; + } + /* count only confirmed enc/salt tuples */ + n++; + } + + keys->nkeys = n; + keys->ksdata = ksdata; + + return n; +} + +int create_keys(krb5_context krbctx, + krb5_principal princ, + char *password, + const char *enctypes_string, + struct keys_container *keys, + char **err_msg) +{ + struct krb_key_salt *ksdata; + krb5_error_code krberr; + krb5_data key_password; + krb5_data *realm = NULL; + int i, nkeys; + int ret; + + *err_msg = NULL; + + ret = prep_ksdata(krbctx, enctypes_string, keys, err_msg); + if (ret == 0) return 0; + + ksdata = keys->ksdata; + nkeys = keys->nkeys; + + if (password) { + key_password.data = password; + key_password.length = strlen(password); + + realm = krb5_princ_realm(krbctx, princ); + } + + for (i = 0; i < nkeys; i++) { + krb5_data *salt; + + if (!password) { + /* cool, random keys */ + krberr = krb5_c_make_random_key(krbctx, + ksdata[i].enctype, + &ksdata[i].key); + if (krberr) { + *err_msg = _("Failed to create random key!\n"); + return 0; + } + /* set the salt to NO_SALT as the key was random */ + ksdata[i].salttype = NO_SALT; + continue; + } + + /* Make keys using password and required salt */ + switch (ksdata[i].salttype) { + case KRB5_KDB_SALTTYPE_ONLYREALM: + krberr = krb5_copy_data(krbctx, realm, &salt); + if (krberr) { + *err_msg = _("Failed to create key!\n"); + return 0; + } + + ksdata[i].salt.length = salt->length; + ksdata[i].salt.data = malloc(salt->length); + if (!ksdata[i].salt.data) { + *err_msg = _("Out of memory!\n"); + return 0; + } + memcpy(ksdata[i].salt.data, salt->data, salt->length); + krb5_free_data(krbctx, salt); + break; + + case KRB5_KDB_SALTTYPE_NOREALM: + krberr = ipa_krb5_principal2salt_norealm(krbctx, princ, + &ksdata[i].salt); + if (krberr) { + *err_msg = _("Failed to create key!\n"); + return 0; + } + break; + + case KRB5_KDB_SALTTYPE_NORMAL: + krberr = krb5_principal2salt(krbctx, princ, &ksdata[i].salt); + if (krberr) { + *err_msg = _("Failed to create key!\n"); + return 0; + } + break; + + /* no KRB5_KDB_SALTTYPE_V4, we do not support krb v4 */ + + case KRB5_KDB_SALTTYPE_AFS3: + /* Comment from MIT sources: + * * Why do we do this? Well, the afs_mit_string_to_key + * * needs to use strlen, and the realm is not NULL + * * terminated.... + */ + ksdata[i].salt.data = (char *)malloc(realm->length + 1); + if (NULL == ksdata[i].salt.data) { + *err_msg = _("Out of memory!\n"); + return 0; + } + memcpy((char *)ksdata[i].salt.data, + (char *)realm->data, realm->length); + ksdata[i].salt.data[realm->length] = '\0'; + /* AFS uses a special length (UGLY) */ + ksdata[i].salt.length = SALT_TYPE_AFS_LENGTH; + break; + + default: + *err_msg = _("Bad or unsupported salt type.\n"); +/* FIXME: + fprintf(stderr, _("Bad or unsupported salt type (%d)!\n"), + ksdata[i].salttype); +*/ + return 0; + } + + krberr = krb5_c_string_to_key(krbctx, + ksdata[i].enctype, + &key_password, + &ksdata[i].salt, + &ksdata[i].key); + if (krberr) { + *err_msg = _("Failed to create key!\n"); + return 0; + } + + /* set back salt length to real value if AFS3 */ + if (ksdata[i].salttype == KRB5_KDB_SALTTYPE_AFS3) { + ksdata[i].salt.length = realm->length; + } + } + + return nkeys; +} + diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h index f0513d109..97ffc47b5 100644 --- a/util/ipa_krb5.h +++ b/util/ipa_krb5.h @@ -4,6 +4,30 @@ #include #include +struct krb_key_salt { + krb5_enctype enctype; + krb5_int32 salttype; + krb5_keyblock key; + krb5_data salt; +}; + +struct keys_container { + krb5_int32 nkeys; + struct krb_key_salt *ksdata; +}; + +/* Salt types */ +#define NO_SALT -1 +#define KRB5_KDB_SALTTYPE_NORMAL 0 +#define KRB5_KDB_SALTTYPE_V4 1 +#define KRB5_KDB_SALTTYPE_NOREALM 2 +#define KRB5_KDB_SALTTYPE_ONLYREALM 3 +#define KRB5_KDB_SALTTYPE_SPECIAL 4 +#define KRB5_KDB_SALTTYPE_AFS3 5 + +#define KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1" +#define KEYTAB_RET_OID "2.16.840.1.113730.3.8.10.2" + void ipa_krb5_free_ktypes(krb5_context context, krb5_enctype *val); @@ -36,4 +60,16 @@ krb5_error_code filter_key_salt_tuples(krb5_context context, krb5_key_salt_tuple *req, int n_req, krb5_key_salt_tuple *supp, int n_supp, krb5_key_salt_tuple **res, int *n_res); + +void free_keys_contents(krb5_context krbctx, struct keys_container *keys); + +struct berval *create_key_control(struct keys_container *keys, + const char *principalName); + +int create_keys(krb5_context krbctx, + krb5_principal princ, + char *password, + const char *enctypes_string, + struct keys_container *keys, + char **err_msg); #endif /* __IPA_KRB5_H_ */ -- cgit